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