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