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