4cee3e17c10c2ad815a448544cb250ce698405b2
[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     try {
337         CAudioIO::pause();
338
339         internalLock();
340
341         /* Updates ASM to STOP */
342         if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
343             mpAudioSessionHandler->updateStop();
344         }
345
346         internalUnlock();
347
348         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
349     } catch (CAudioError e) {
350         internalUnlock();
351         throw e;
352     }
353 }
354
355 void CAudioInput::resume() throw(CAudioError) {
356     if (__IsInit() == false || __IsReady() == false) {
357         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
358     }
359
360     try {
361         internalLock();
362
363         if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
364             /* Updates ASM to PLAYING */
365             mpAudioSessionHandler->updatePlaying();
366         }
367
368         internalUnlock();
369
370         CAudioIO::resume();
371
372         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
373     } catch (CAudioError e) {
374         internalUnlock();
375         throw e;
376     }
377 }
378
379 void CAudioInput::drain() throw(CAudioError) {
380     THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "Did not support drain of CAudioInput");
381 }
382
383 void CAudioInput::flush() throw(CAudioError) {
384     if (__IsInit() == false || __IsReady() == false) {
385         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
386     }
387
388     try {
389         CAudioIO::flush();
390     } catch (CAudioError e) {
391         throw e;
392     }
393 }
394
395 int CAudioInput::getBufferSize() throw(CAudioError) {
396     if (__IsInit() == false) {
397         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput");
398     }
399
400     if (__IsReady() == false) {
401         AUDIO_IO_LOGD("Warning: Did not prepare CAudioInput, then return zero");
402         return 0;
403     }
404
405     int size = 0;
406
407     try {
408         size = mpPulseAudioClient->getBufferSize();
409     } catch (CAudioError err) {
410         throw err;
411     }
412
413     return size;
414 }
415
416 void CAudioInput::setStreamCallback(SStreamCallback callback) throw(CAudioError) {
417     if (__IsInit() == false) {
418         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput");
419     }
420
421     if (callback.onStream == NULL) {
422         AUDIO_IO_LOGD("__mIsUsedSyncRead = true");
423         __mIsUsedSyncRead = true;
424     } else {
425         AUDIO_IO_LOGD("__mIsUsedSyncRead = false");
426         __mIsUsedSyncRead = false;
427     }
428
429     CAudioIO::setStreamCallback(callback);
430 }
431
432 size_t CAudioInput::read(void* buffer, size_t length) throw(CAudioError) {
433     if (__IsInit() == false || __IsReady() == false) {
434         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
435     }
436
437     if (buffer == NULL) {
438         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameters are NULL buffer:%p", buffer);
439     }
440
441     /* Checks synchronous flag */
442     if (__mIsUsedSyncRead == false) {
443         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "Invalid operation of read() if receive stream callback");
444     }
445
446     int ret = 0;
447
448     try {
449         internalLock();
450
451         // If another thread did call unprepare, do not read
452         if (mpPulseAudioClient == NULL)
453             THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
454
455         // Block until read done
456         ret = mpPulseAudioClient->read(buffer, length);
457
458         internalUnlock();
459     } catch (CAudioError e) {
460         internalUnlock();
461         throw e;
462     }
463
464     return ret;
465 }
466
467 int CAudioInput::peek(const void** buffer, size_t* length) throw(CAudioError) {
468     if (__IsInit() == false || __IsReady() == false) {
469         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
470     }
471
472     if (buffer == NULL || length == NULL) {
473         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameters are NULL buffer:%p, length:%p", buffer, length);
474     }
475
476     /* Checks synchronous flag */
477     if (__mIsUsedSyncRead == true) {
478         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "Invalid operation of peek() if does not receive a stream callback");
479     }
480
481     int ret = 0;
482
483     try {
484         ret = mpPulseAudioClient->peek(buffer, length);
485     } catch (CAudioError e) {
486         throw e;
487     }
488
489     return ret;
490 }
491
492 int CAudioInput::drop() throw(CAudioError) {
493     if (__IsInit() == false || __IsReady() == false) {
494         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
495     }
496
497     /* Checks synchronous flag */
498     if (__mIsUsedSyncRead == true) {
499         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "Invalid operation of drop() if does not receive a stream callback");
500     }
501
502     int ret = 0;
503
504     try {
505         ret = mpPulseAudioClient->drop();
506     } catch (CAudioError e) {
507         throw e;
508     }
509
510     return ret;
511 }