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