3887ddb6eb4d2dbf082e7268841e8b38b7a9c97f
[platform/core/api/audio-io.git] / src / cpp / CAudioInput.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 <pulse/pulseaudio.h>
19 #include "CAudioIODef.h"
20
21 #define RECORDER_PRIVILEGE "http://tizen.org/privilege/recorder"
22 #define CLIENT_NAME "AUDIO_IO_PA_CLIENT"
23
24 using namespace std;
25 using namespace tizen_media_audio;
26
27 struct PrivilegeData {
28     bool isPrivilegeAllowed;
29     pa_threaded_mainloop *paMainloop;
30 };
31
32 /**
33  * class CAudioInput inherited by CAudioIO
34  */
35 CAudioInput::CAudioInput(CAudioInfo& info) :
36     CAudioIO(info),
37     __mIsUsedSyncRead(true),
38     __mIsInit(false) {
39 }
40
41 CAudioInput::CAudioInput(
42         unsigned int            sampleRate,
43         CAudioInfo::EChannel    channel,
44         CAudioInfo::ESampleType type,
45         CAudioInfo::EAudioType  audioType) :
46     __mIsUsedSyncRead(true),
47     __mIsInit(false) {
48     mAudioInfo = CAudioInfo(sampleRate, channel, type, audioType, -1);
49 }
50
51 CAudioInput::~CAudioInput() {
52 }
53
54 void CAudioInput::onStream(CPulseAudioClient* pClient, size_t length) {
55     assert(pClient);
56
57     /*
58      * Does not call CAudioIO::onStream() for synchronization
59      * if a user is using read()
60      */
61     if (__mIsUsedSyncRead == true) {
62 #ifdef _AUDIO_IO_DEBUG_TIMING_
63         AUDIO_IO_LOGD("Sync Read Mode! - pClient:[%p], length:[%d]", pClient, length);
64 #endif
65         return;
66     }
67
68     /*
69      * Accrues callback function
70      */
71 #ifdef _AUDIO_IO_DEBUG_TIMING_
72     AUDIO_IO_LOGD("pClient:[%p], length:[%d]", pClient, length);
73 #endif
74     CAudioIO::onStream(pClient, length);
75 }
76
77 void CAudioInput::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) {
78     assert(pHandler);
79     AUDIO_IO_LOGD("[pHandler:0x%x], [focus_type:%d], [state:%d], [reason_for_change:%s], [additional_info:%s]", pHandler, focus_type, state, reason_for_change, additional_info);
80     CAudioIO::onInterrupt(pHandler, id, focus_type, state, reason_for_change, additional_info);
81 }
82
83 void CAudioInput::onSignal(CAudioSessionHandler* pHandler, mm_sound_signal_name_t signal, int value) {
84     assert(pHandler);
85     AUDIO_IO_LOGD("[pHandler:0x%x], [signal:%d], [value:%d]", pHandler, signal, value);
86     CAudioIO::onSignal(pHandler, signal, value);
87 }
88
89 void CAudioInput::__setInit(bool flag) {
90     __mIsInit = flag;
91 }
92
93 bool CAudioInput::__IsInit() {
94     return (CAudioIO::isInit() == true && __mIsInit == true);
95 }
96
97 bool CAudioInput::__IsReady() {
98     return CAudioIO::IsReady();
99 }
100
101 static void __contextStateChangeCb(pa_context* c, void* user_data) {
102     pa_threaded_mainloop *paMainloop = static_cast<pa_threaded_mainloop*>(user_data);
103     assert(paMainloop);
104     assert(c);
105
106     switch (pa_context_get_state(c)) {
107     case PA_CONTEXT_READY:
108         AUDIO_IO_LOGD("The context is ready");
109         pa_threaded_mainloop_signal(paMainloop, 0);
110         break;
111
112     case PA_CONTEXT_FAILED:
113     case PA_CONTEXT_TERMINATED:
114         AUDIO_IO_LOGD("The context is lost");
115         pa_threaded_mainloop_signal(paMainloop, 0);
116         break;
117
118     case PA_CONTEXT_UNCONNECTED:
119     case PA_CONTEXT_CONNECTING:
120     case PA_CONTEXT_AUTHORIZING:
121     case PA_CONTEXT_SETTING_NAME:
122         break;
123     }
124 }
125
126 static void __checkPrivilegeCb(pa_context *c, int success, void *user_data) {
127     AUDIO_IO_LOGD("pa_context[%p], success[%d], user_data[%p]", c, success, user_data);
128     assert(c);
129     assert(user_data);
130
131     PrivilegeData *prData = static_cast<PrivilegeData*>(user_data);
132     prData->isPrivilegeAllowed = success ? true : false;
133
134     pa_threaded_mainloop_signal(prData->paMainloop, 0);
135 }
136
137 static bool __IsPrivilegeAllowed() {
138     pa_operation *o;
139     pa_context *c;
140     int err = 0;
141     PrivilegeData prData;
142
143     prData.paMainloop = pa_threaded_mainloop_new();
144     if (prData.paMainloop == NULL)
145         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_threaded_mainloop_new()");
146     c = pa_context_new(pa_threaded_mainloop_get_api(prData.paMainloop), CLIENT_NAME);
147     if (c == NULL)
148         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_context_new()");
149
150     pa_context_set_state_callback(c, __contextStateChangeCb, prData.paMainloop);
151
152     if (pa_context_connect(c, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0)
153         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_context_connect()");
154
155     pa_threaded_mainloop_lock(prData.paMainloop);
156
157     if (pa_threaded_mainloop_start(prData.paMainloop) < 0) {
158         pa_threaded_mainloop_unlock(prData.paMainloop);
159         THROW_ERROR_MSG(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_threaded_mainloop_start()");
160     }
161
162     while (true) {
163         pa_context_state_t state;
164         state = pa_context_get_state(c);
165
166         if (state == PA_CONTEXT_READY)
167             break;
168
169         if (!PA_CONTEXT_IS_GOOD(state)) {
170             err = pa_context_errno(c);
171             pa_threaded_mainloop_unlock(prData.paMainloop);
172             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "pa_context's state is not good : err[%d]", err);
173         }
174
175         /* Wait until the context is ready */
176         pa_threaded_mainloop_wait(prData.paMainloop);
177     }
178
179     o = pa_context_check_privilege(c, RECORDER_PRIVILEGE, __checkPrivilegeCb, &prData);
180     while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
181         pa_threaded_mainloop_wait(prData.paMainloop);
182
183     pa_threaded_mainloop_unlock(prData.paMainloop);
184     pa_threaded_mainloop_stop(prData.paMainloop);
185     pa_context_disconnect(c);
186     pa_context_unref(c);
187     pa_threaded_mainloop_free(prData.paMainloop);
188
189     return prData.isPrivilegeAllowed;
190 }
191
192 void CAudioInput::initialize() throw(CAudioError) {
193     if (__IsInit() == true) {
194         return;
195     }
196
197     try {
198         CAudioIO::initialize();
199         if (__IsPrivilegeAllowed() == false) {
200             THROW_ERROR_MSG(CAudioError::EError::ERROR_PERMISSION_DENIED, "No privilege for record");
201         }
202
203         // Create ASM Handler
204         mpAudioSessionHandler = new CAudioSessionHandler(CAudioSessionHandler::EAudioSessionType::AUDIO_SESSION_TYPE_CAPTURE, mAudioInfo, this);
205         if (mpAudioSessionHandler == NULL) {
206             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed to allocate CAudioSessionHandler object");
207         }
208
209         // Initialize ASM Handler
210         mpAudioSessionHandler->initialize();
211
212         __setInit(true);
213         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
214     } catch (CAudioError err) {
215         finalize();
216         throw err;
217     }
218 }
219
220 void CAudioInput::finalize() {
221     if (__IsInit() == false) {
222         AUDIO_IO_LOGD("Did not initialize");
223         return;
224     }
225
226     SAFE_FINALIZE(mpAudioSessionHandler);
227     SAFE_DELETE(mpAudioSessionHandler);
228
229     CAudioIO::finalize();
230
231     __setInit(false);
232 }
233
234 void CAudioInput::prepare() throw(CAudioError) {
235     if (__IsInit() == false) {
236         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput");
237     }
238
239     if (__IsReady() == true) {
240         AUDIO_IO_LOGD("Already prepared CAudioInput");
241         return;
242     }
243
244     try {
245         internalLock();
246
247         // Check to invalid AudioType
248         CAudioInfo::EAudioType audioType = mAudioInfo.getAudioType();
249         if (audioType < CAudioInfo::EAudioType::AUDIO_IN_TYPE_MEDIA || audioType > CAudioInfo::EAudioType::AUDIO_IN_TYPE_LOOPBACK) {
250             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "The audioType is invalid [type:%d]", static_cast<int>(audioType));
251         }
252
253         if (mpAudioSessionHandler->getId() < 0) {  // Did not registerSound()
254             // Check session to skip registration
255             if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
256                 // Register ASM Listener
257                 AUDIO_IO_LOGD("Register ASM Listener");
258                 mpAudioSessionHandler->registerSound();
259             }
260         }
261
262         // Init StreamSpec
263         AUDIO_IO_LOGD("Set Stream Spec : CPulseStreamSpec::STREAM_LATENCY_INPUT_DEFAULT");
264         CPulseStreamSpec::EStreamLatency streamSpec = CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_INPUT_DEFAULT;
265         CPulseStreamSpec spec(streamSpec, mAudioInfo);
266
267         // Create PulseAudio Handler
268         mpPulseAudioClient = new CPulseAudioClient(CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_RECORD, spec, this);
269         if (mpPulseAudioClient == NULL) {
270             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed to allocate CPulseAudioClient object");
271         }
272
273         // Initialize PulseAudio Handler
274         mpPulseAudioClient->initialize();
275
276         if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
277             /* Updates ASM to PLAYING */
278             mpAudioSessionHandler->updatePlaying();
279         }
280
281         internalUnlock();
282
283         // Do Prepare
284         CAudioIO::prepare();
285     } catch (CAudioError e) {
286         internalUnlock();
287         throw e;
288     }
289 }
290
291 void CAudioInput::unprepare() throw(CAudioError) {
292     if (__IsInit() == false) {
293         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput");
294     }
295
296     if (__IsReady() == false) {
297         AUDIO_IO_LOGD("Already unprepared");
298         return;
299     }
300
301     try {
302         // Do unprepare
303         CAudioIO::unprepare();
304
305         internalLock();
306
307         SAFE_FINALIZE(mpPulseAudioClient);
308         SAFE_DELETE(mpPulseAudioClient);
309
310         if (mpAudioSessionHandler->getId() >= 0) {
311             /* Updates ASM to STOP */
312             if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
313                 mpAudioSessionHandler->updateStop();
314             }
315
316             bool isSkip = mpAudioSessionHandler->isSkipSessionEvent();
317             if (isSkip == false) {
318                 mpAudioSessionHandler->unregisterSound();
319             }
320         }
321
322         internalUnlock();
323
324         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
325     } catch (CAudioError e) {
326         internalUnlock();
327         throw e;
328     }
329 }
330
331 void CAudioInput::pause() throw(CAudioError) {
332     if (__IsInit() == false || __IsReady() == false) {
333         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
334     }
335
336     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING) {
337         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE,
338                         "Can't pause if not in Running state");
339     }
340
341     if (mpPulseAudioClient->isInThread() == true) {
342         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't pause in thread");
343     }
344
345     try {
346         CAudioIO::pause();
347
348         internalLock();
349
350         /* Updates ASM to STOP */
351         if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
352             mpAudioSessionHandler->updateStop();
353         }
354
355         internalUnlock();
356
357         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
358     } catch (CAudioError e) {
359         internalUnlock();
360         throw e;
361     }
362 }
363
364 void CAudioInput::resume() throw(CAudioError) {
365     if (__IsInit() == false || __IsReady() == false) {
366         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
367     }
368
369     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED) {
370         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE,
371                         "Can't resume if not in Paused state");
372     }
373
374     if (mpPulseAudioClient->isInThread() == true) {
375         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't resume in thread");
376     }
377
378     try {
379         internalLock();
380
381         if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
382             /* Updates ASM to PLAYING */
383             mpAudioSessionHandler->updatePlaying();
384         }
385
386         internalUnlock();
387
388         CAudioIO::resume();
389
390         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
391     } catch (CAudioError e) {
392         internalUnlock();
393         throw e;
394     }
395 }
396
397 void CAudioInput::drain() throw(CAudioError) {
398     THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "Did not support drain of CAudioInput");
399 }
400
401 void CAudioInput::flush() throw(CAudioError) {
402     if (__IsInit() == false || __IsReady() == false) {
403         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
404     }
405
406     try {
407         CAudioIO::flush();
408     } catch (CAudioError e) {
409         throw e;
410     }
411 }
412
413 int CAudioInput::getBufferSize() throw(CAudioError) {
414     if (__IsInit() == false) {
415         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput");
416     }
417
418     /* FIXME : return calculated size here to satisfy backward compatibility */
419     return (mAudioInfo.getSampleRate() * DEFAULT_PERIOD_SIZE) / 1000 * mAudioInfo.getSampleSize();
420 }
421
422 void CAudioInput::setStreamCallback(SStreamCallback callback) throw(CAudioError) {
423     if (__IsInit() == false) {
424         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput");
425     }
426
427     if (callback.onStream == NULL) {
428         AUDIO_IO_LOGD("__mIsUsedSyncRead = true");
429         __mIsUsedSyncRead = true;
430     } else {
431         AUDIO_IO_LOGD("__mIsUsedSyncRead = false");
432         __mIsUsedSyncRead = false;
433     }
434
435     CAudioIO::setStreamCallback(callback);
436 }
437
438 size_t CAudioInput::read(void* buffer, size_t length) throw(CAudioError) {
439     if (__IsInit() == false || __IsReady() == false) {
440         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
441     }
442
443     if (buffer == NULL) {
444         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameters are NULL buffer:%p", buffer);
445     }
446     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING) {
447         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
448                         "Can't read if not in Running state");
449     }
450     /* Checks synchronous flag */
451     if (__mIsUsedSyncRead == false) {
452         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "Invalid operation of read() if receive stream callback");
453     }
454
455     int ret = 0;
456
457     try {
458         internalLock();
459
460         // If another thread did call unprepare, do not read
461         if (mpPulseAudioClient == NULL)
462             THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
463
464         // Block until read done
465         ret = mpPulseAudioClient->read(buffer, length);
466
467         internalUnlock();
468     } catch (CAudioError e) {
469         internalUnlock();
470         throw e;
471     }
472
473     return ret;
474 }
475
476 int CAudioInput::peek(const void** buffer, size_t* length) throw(CAudioError) {
477     if (__IsInit() == false || __IsReady() == false) {
478         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
479     }
480
481     if (buffer == NULL || length == NULL) {
482         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameters are NULL buffer:%p, length:%p", buffer, length);
483     }
484
485     /* Checks synchronous flag */
486     if (__mIsUsedSyncRead == true) {
487         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "Invalid operation of peek() if does not receive a stream callback");
488     }
489
490     int ret = 0;
491
492     try {
493         ret = mpPulseAudioClient->peek(buffer, length);
494     } catch (CAudioError e) {
495         throw e;
496     }
497
498     return ret;
499 }
500
501 int CAudioInput::drop() throw(CAudioError) {
502     if (__IsInit() == false || __IsReady() == false) {
503         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
504     }
505
506     /* Checks synchronous flag */
507     if (__mIsUsedSyncRead == true) {
508         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "Invalid operation of drop() if does not receive a stream callback");
509     }
510
511     int ret = 0;
512
513     try {
514         ret = mpPulseAudioClient->drop();
515     } catch (CAudioError e) {
516         throw e;
517     }
518
519     return ret;
520 }