CAudioInfo: Refactor getters to return in group
[platform/core/api/audio-io.git] / src / cpp / CPulseAudioClient.cpp
1 /*
2  git st* 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 "CAudioIODef.h"
19 #include <unistd.h>
20 #include <inttypes.h>
21 #include <string.h>
22 #include <assert.h>
23 #include <algorithm>
24 #include <vector>
25
26 #ifdef ENABLE_DPM
27 #include <dpm/restriction.h>
28 #endif
29
30 using namespace std;
31 using namespace tizen_media_audio;
32
33 /**
34  * class CMainloopLocker
35  */
36 CPulseAudioClient::CPulseThreadLocker::CPulseThreadLocker(pa_threaded_mainloop* mainloop) : ml(nullptr) {
37     if (!mainloop || pa_threaded_mainloop_in_thread(mainloop))
38         return;
39
40     pa_threaded_mainloop_lock(mainloop);
41     ml = mainloop;
42 }
43
44 CPulseAudioClient::CPulseThreadLocker::~CPulseThreadLocker() {
45     if (ml)
46         pa_threaded_mainloop_unlock(const_cast<pa_threaded_mainloop*>(ml));
47     ml = nullptr;
48 }
49
50 /**
51  * class CPulseAudioClient
52  */
53 const char* CPulseAudioClient::CLIENT_NAME = "AUDIO_IO_PA_CLIENT";
54 const char* ec_method = "default";
55 static constexpr auto drain_wait_interval = 10000;
56 static constexpr auto drain_wait_max_count = 30;
57
58 CPulseAudioClient::CPulseAudioClient(
59         EStreamDirection      direction,
60         CPulseStreamSpec&     spec,
61         IPulseStreamListener* listener) :
62     __mDirection(direction),
63     __mSpec(spec),
64     __mpListener(listener) {
65 }
66
67 CPulseAudioClient::~CPulseAudioClient() {
68     try {
69         finalize();
70     } catch (const CAudioError& e) {
71         AUDIO_IO_LOGE("%s", e.getErrorMsg()); //LCOV_EXCL_LINE
72     }
73 }
74
75 void CPulseAudioClient::__contextStateChangeCb(pa_context* c, void* user_data) {
76     auto pClient = static_cast<CPulseAudioClient*>(user_data);
77     assert(pClient);
78     assert(c);
79
80     switch (pa_context_get_state(c)) {
81     case PA_CONTEXT_READY:
82         AUDIO_IO_LOGD("pa_context[%p] is ready", c);
83         pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
84         break;
85
86     case PA_CONTEXT_FAILED:
87     case PA_CONTEXT_TERMINATED:
88         AUDIO_IO_LOGD("pa_context[%p] is lost", c);
89         pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
90         break;
91
92     case PA_CONTEXT_UNCONNECTED:
93     case PA_CONTEXT_CONNECTING:
94     case PA_CONTEXT_AUTHORIZING:
95     case PA_CONTEXT_SETTING_NAME:
96         break;
97     }
98 }
99
100 //LCOV_EXCL_START
101 static bool __is_microphone_restricted(void) {
102     int state = 1;
103 #ifdef ENABLE_DPM
104     device_policy_manager_h dpm_h = nullptr;
105     int ret = 0;
106
107     if ((dpm_h = dpm_manager_create())) {
108         /* state: 0(disallowed), 1(allowed) */
109         if ((ret = dpm_restriction_get_microphone_state(dpm_h, &state)))
110             AUDIO_IO_LOGE("Failed to dpm_restriction_get_microphone_state(), ret(0x%x)", ret);
111         dpm_manager_destroy(dpm_h);
112         AUDIO_IO_LOGD("microphone restriction state: %d(1:allowed, 0:disallowed)", state);
113     } else {
114         AUDIO_IO_LOGE("Failed to dpm_manager_create()");
115     }
116 #endif
117     return (state ? false : true);
118 }
119 //LCOV_EXCL_STOP
120
121 void CPulseAudioClient::__streamStateChangeCb(pa_stream* s, void* user_data) {
122     assert(s);
123     assert(user_data);
124
125     auto pClient = static_cast<CPulseAudioClient*>(user_data);
126     pa_stream_state_t state = pa_stream_get_state(s);
127
128     switch (state) {
129     case PA_STREAM_UNCONNECTED:
130         break;
131
132     case PA_STREAM_CREATING:
133         break;
134
135     case PA_STREAM_READY:
136         AUDIO_IO_LOGD("pClient[%p] pa_stream[%p] is READY[%d]", pClient, s, PA_STREAM_READY);
137         pClient->__mpListener->setState(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
138         pClient->__mpListener->onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
139         if (pClient->__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK)
140             pClient->__mIsFirstStream = true;
141         pClient->__mIsInit = true;
142
143         AUDIO_IO_LOGD("pClient[%p] pa_stream[%p] now signaling...", pClient, s);
144         pa_threaded_mainloop_signal(pClient->__mpMainloop, 1);
145         AUDIO_IO_LOGD("pClient[%p] pa_stream[%p] signal accepted", pClient, s);
146         break;
147
148 //LCOV_EXCL_START
149     case PA_STREAM_FAILED:
150         AUDIO_IO_LOGD("pClient[%p] pa_stream[%p] is FAILED[%d]", pClient, s, PA_STREAM_FAILED);
151         pClient->__mpListener->setState(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
152         pClient->__mpListener->onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE,
153                                               __is_microphone_restricted());
154
155         AUDIO_IO_LOGD("pClient[%p] pa_stream[%p] now signaling...", pClient, s);
156         pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
157         break;
158 //LCOV_EXCL_STOP
159
160     case PA_STREAM_TERMINATED:
161         AUDIO_IO_LOGD("pClient[%p] pa_stream[%p] is TERMINATED[%d]", pClient, s, PA_STREAM_TERMINATED);
162         pClient->__mpListener->setState(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
163         pClient->__mpListener->onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
164
165         AUDIO_IO_LOGD("pClient[%p] pa_stream[%p] now signaling...", pClient, s);
166         pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
167         break;
168
169     default:
170         AUDIO_IO_LOGW("Unexpected state[%d]", state);
171         break;
172     }
173
174     AUDIO_IO_LOGD("pClient[%p] pa_stream[%p] state[%d] done", pClient, s, state);
175 }
176
177 void CPulseAudioClient::__streamCaptureCb(pa_stream* s, size_t length, void* user_data) {
178     assert(s);
179     assert(user_data);
180
181     auto pClient = static_cast<CPulseAudioClient*>(user_data);
182     assert(pClient->__mpListener);
183     assert(pClient->__mpMainloop);
184
185     if (pClient->__mIsUsedSyncRead)
186         pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
187
188     pClient->__mpListener->onStream(pClient, length);
189 }
190
191 static void __dummy_write(pa_stream* s, size_t length) {
192     pa_stream_write(s, std::vector<char>(length).data(), length, NULL, 0LL, PA_SEEK_RELATIVE);
193 }
194
195 void CPulseAudioClient::__streamPlaybackCb(pa_stream* s, size_t length, void* user_data) {
196     assert(s);
197     assert(user_data);
198
199     auto pClient = static_cast<CPulseAudioClient*>(user_data);
200     assert(pClient->__mpListener);
201
202 #ifndef DISABLE_MOBILE_BACK_COMP
203     if (!pClient->__mIsInit) {
204         AUDIO_IO_LOGD("pa_stream[%p] is on the way to create : Write dummy, length[%zu]", s, length);
205         __dummy_write(s, length);
206         return;
207     }
208     if (pClient->isCorked()) {
209         AUDIO_IO_LOGD("pa_stream[%p] is CORKED : Write dummy, length[%zu]", s, length);
210         __dummy_write(s, length);
211         return;
212     }
213 #endif
214
215     pClient->__mpListener->onStream(pClient, length);
216
217 #ifndef DISABLE_MOBILE_BACK_COMP
218     /* If stream is not written in first callback during prepare,
219        then write dummy data to ensure the start */
220     if (pClient->__mSpec.getStreamLatency() == CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_OUTPUT_DEFAULT_ASYNC &&
221         pClient->__mIsFirstStream) {
222         AUDIO_IO_LOGW("[async] pa_stream[%p] : not written in the first callback during prepare, write dummy of length[%zu]",
223                       s, length);
224         __dummy_write(s, length);
225         pClient->__mIsFirstStream = false;
226     }
227 #endif
228 }
229
230 void CPulseAudioClient::__streamLatencyUpdateCb(pa_stream* s, void* user_data) {
231     assert(s);
232     assert(user_data);
233
234     auto pClient = static_cast<CPulseAudioClient*>(user_data);
235
236 #ifdef _AUDIO_IO_DEBUG_TIMING_
237     /* FIXME : print some useful latency info */
238     AUDIO_IO_LOGD("pClient[%p] pa_stream[%p] latency updated", pClient, s);
239 #endif
240
241     pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
242 }
243
244 void CPulseAudioClient::__streamStartedCb(pa_stream* s, void* user_data) {
245     assert(s);
246     assert(user_data);
247
248     auto pClient = static_cast<CPulseAudioClient*>(user_data);
249
250     AUDIO_IO_LOGD("pClient[%p] pa_stream[%p] started", pClient, s);
251
252     pClient->__mIsStarted = true;
253 }
254
255 void CPulseAudioClient::__streamUnderflowCb(pa_stream* s, void* user_data) {
256     assert(s);
257     assert(user_data);
258
259     auto pClient = static_cast<CPulseAudioClient*>(user_data);
260
261     AUDIO_IO_LOGD("pClient[%p] pa_stream[%p] UnderFlow...", pClient, s);
262
263     pClient->__mIsStarted = false;
264 }
265
266 void CPulseAudioClient::__streamOverflowCb(pa_stream* s, void* user_data) {
267     assert(s);
268     assert(user_data);
269
270     auto pClient = static_cast<CPulseAudioClient*>(user_data);
271
272     AUDIO_IO_LOGD("pClient[%p] pa_stream[%p] OverFlow...", pClient, s);
273 }
274
275 //LCOV_EXCL_START
276 void CPulseAudioClient::__streamEventCb(pa_stream* s, const char* name, pa_proplist* pl, void* user_data) {
277     assert(s);
278     assert(user_data);
279
280     auto pClient = static_cast<CPulseAudioClient*>(user_data);
281
282     AUDIO_IO_LOGW("pClient[%p] pa_stream[%p] : name[%s], proplist[%p]", pClient, s, name, pl);
283
284     if (strcmp(name, PA_STREAM_EVENT_POP_TIMEOUT) == 0) {
285         pa_operation* o = pa_stream_cork(pClient->__mpStream, 1, NULL, NULL);
286         if (!o) {
287             AUDIO_IO_LOGE("pa_stream[%p] : cork failed", pClient->__mpStream);
288             return;
289         }
290         pa_operation_unref(o);
291     }
292 }
293 //LCOV_EXCL_STOP
294
295 void CPulseAudioClient::__successStreamCb(pa_stream* s, int success, void* user_data) {
296     assert(s);
297     assert(user_data);
298
299     auto pClient = static_cast<CPulseAudioClient*>(user_data);
300
301     AUDIO_IO_LOGD("pClient[%p] pa_stream[%p] success[%d] user_data[%p]", pClient, s, success, user_data);
302
303     pClient->__mIsOperationSuccess = static_cast<bool>(success);
304
305     /* FIXME : verify following action without any waiting */
306     pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
307 }
308
309 //LCOV_EXCL_START
310 void CPulseAudioClient::__successDrainCbInThread(pa_stream* s, int success, void* user_data) {
311     assert(s);
312     assert(user_data);
313
314     auto pClient = static_cast<CPulseAudioClient*>(user_data);
315
316     AUDIO_IO_LOGD("pClient[%p] pa_stream[%p] success[%d] user_data[%p]", pClient, s, success, user_data);
317
318     pClient->__mIsOperationSuccess = static_cast<bool>(success);
319     pClient->__mIsDraining = false;
320 }
321 //LCOV_EXCL_STOP
322
323 void CPulseAudioClient::__successDrainCb(pa_stream* s, int success, void* user_data) {
324     assert(s);
325     assert(user_data);
326
327     auto pClient = static_cast<CPulseAudioClient*>(user_data);
328
329     AUDIO_IO_LOGD("pClient[%p] pa_stream[%p] success[%d] user_data[%p]", pClient, s, success, user_data);
330
331     pClient->__mIsOperationSuccess = static_cast<bool>(success);
332     pClient->__mIsDraining = false;
333
334     pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
335 }
336
337 void CPulseAudioClient::__successVolumeCb(pa_context* c, int success, void* user_data) {
338     AUDIO_IO_LOGD("pa_context[%p] success[%d] user_data[%p]", c, success, user_data);
339 }
340
341 void CPulseAudioClient::resetStreamCallbacks() {
342     if (!__mpStream)
343         return;
344
345     if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK)
346         pa_stream_set_write_callback(__mpStream, NULL, NULL);
347     else
348         pa_stream_set_read_callback(__mpStream, NULL, NULL);
349
350     pa_stream_set_latency_update_callback(__mpStream, NULL, NULL);
351     pa_stream_set_event_callback(__mpStream, NULL, NULL);
352 }
353
354 void CPulseAudioClient::resetInternalObjects() {
355     if (__mpMainloop)
356         pa_threaded_mainloop_stop(__mpMainloop);
357
358     if (__mpStream) {
359         pa_stream_disconnect(__mpStream);
360         pa_stream_unref(__mpStream);
361         __mpStream = nullptr;
362     }
363
364     if (__mpContext) {
365         pa_context_disconnect(__mpContext);
366         pa_context_unref(__mpContext);
367         __mpContext = nullptr;
368     }
369
370     if (__mpMainloop) {
371         pa_threaded_mainloop_free(__mpMainloop);
372         __mpMainloop = nullptr;
373     }
374
375     if (__mpPropList) {
376         pa_proplist_free(__mpPropList);
377         __mpPropList = nullptr;
378     }
379 }
380
381 void CPulseAudioClient::initialize() {
382     if (__mIsInit)
383         return;
384
385     int ret = 0;
386     int err = 0;
387
388     try {
389         // Allocates PA proplist
390         __mpPropList = pa_proplist_new();
391         if (!__mpPropList)
392             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_proplist_new()"); //LCOV_EXCL_LINE
393
394         // Adds values on proplist for delivery to PULSEAUDIO
395         auto [ streamType, streamIndex ] = __mSpec.getAudioInfo().getStreamProperties();
396
397         pa_proplist_sets(__mpPropList, PA_PROP_MEDIA_ROLE, streamType);
398         if (streamIndex >= 0)
399             pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_PARENT_ID, "%d", streamIndex);
400
401         if (__mDirection == EStreamDirection::STREAM_DIRECTION_RECORD) {
402             /* Noise-suppression effect should be set first. */
403             std::string method = __mSpec.getAudioInfo().getEffectMethod();
404             auto [ method_reference, device_id ] = __mSpec.getAudioInfo().getEffectMethodWithReference();
405             std::string method_all = method + method_reference;
406
407             if (!method_all.empty()) {
408                 pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_PREPROCESSOR_METHOD, "%s", method_all.c_str());
409                 pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_ECHO_CANCEL_REFERENCE_DEVICE, "%d", device_id);
410
411                 AUDIO_IO_LOGD("preprocess will be set. method[%s], device_id[%d]", method_all.c_str(), device_id);
412             }
413         }
414
415         // Adds latency on proplist for delivery to PULSEAUDIO
416         AUDIO_IO_LOGD("LATENCY : %s[%d]", __mSpec.getStreamLatencyToString(), static_cast<int>(__mSpec.getStreamLatency()));
417         pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_TIZEN_AUDIO_LATENCY, "%s", __mSpec.getStreamLatencyToString());
418
419         // Allocates PA mainloop
420         __mpMainloop = pa_threaded_mainloop_new();
421         if (!__mpMainloop)
422             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_threaded_mainloop_new()"); //LCOV_EXCL_LINE
423
424         // Allocates PA context
425         __mpContext = pa_context_new(pa_threaded_mainloop_get_api(__mpMainloop), CLIENT_NAME);
426         if (!__mpContext)
427             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_context_new()"); //LCOV_EXCL_LINE
428
429         // Sets context state changed callback
430         pa_context_set_state_callback(__mpContext, __contextStateChangeCb, this);
431
432         // Connects this client with PA server
433         if (pa_context_connect(__mpContext, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0)
434             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_context_connect()"); //LCOV_EXCL_LINE
435
436         // LOCK for synchronous connection
437         CPulseThreadLocker locker{__mpMainloop};
438
439         // Start mainloop
440         if (pa_threaded_mainloop_start(__mpMainloop) < 0) {
441 //LCOV_EXCL_START
442             THROW_ERROR_MSG(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_threaded_mainloop_start()");
443 //LCOV_EXCL_STOP
444         }
445
446         // Connection process is asynchronously
447         // So, this function will be waited when occurred context state change event
448         // If I got a signal, do next processing
449         while (true) {
450             pa_context_state_t state;
451             state = pa_context_get_state(__mpContext);
452
453             if (state == PA_CONTEXT_READY)
454                 break;
455
456             if (!PA_CONTEXT_IS_GOOD(state)) {
457 //LCOV_EXCL_START
458                 err = pa_context_errno(__mpContext);
459                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION,
460                                        "pa_context's state is not good : err[%d]", err);
461 //LCOV_EXCL_STOP
462             }
463
464             /* Wait until the context is ready */
465             pa_threaded_mainloop_wait(__mpMainloop);
466         }
467
468         AUDIO_IO_LOGD("pa_context[%p] now ready", __mpContext);
469
470         // Allocates PA stream
471         pa_sample_spec ss   = __mSpec.getSampleSpec();
472         pa_channel_map map  = __mSpec.getChannelMap();
473
474         __mpStream = pa_stream_new_with_proplist(__mpContext, __mSpec.getStreamName(), &ss, &map, __mpPropList);
475         if (!__mpStream) {
476 //LCOV_EXCL_START
477             THROW_ERROR_MSG(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_new_with_proplist()");
478 //LCOV_EXCL_STOP
479         }
480
481         // Sets stream callbacks
482         pa_stream_set_state_callback(__mpStream, __streamStateChangeCb, this);
483         pa_stream_set_read_callback(__mpStream, __streamCaptureCb, this);
484         pa_stream_set_write_callback(__mpStream, __streamPlaybackCb, this);
485         pa_stream_set_latency_update_callback(__mpStream, __streamLatencyUpdateCb, this);
486         pa_stream_set_event_callback(__mpStream, __streamEventCb, this);
487         if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
488             pa_stream_set_started_callback(__mpStream, __streamStartedCb, this);
489             pa_stream_set_underflow_callback(__mpStream, __streamUnderflowCb, this);
490             pa_stream_set_overflow_callback(__mpStream, __streamOverflowCb, this);
491         }
492
493         // Connect stream with PA Server
494
495         if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
496             auto flags = static_cast<pa_stream_flags_t>(
497                     PA_STREAM_INTERPOLATE_TIMING |
498                     PA_STREAM_ADJUST_LATENCY     |
499 #ifndef DISABLE_MOBILE_BACK_COMP
500                     PA_STREAM_START_CORKED       |
501 #endif
502                     PA_STREAM_AUTO_TIMING_UPDATE);
503
504             ret = pa_stream_connect_playback(__mpStream, NULL, NULL, flags, NULL, NULL);
505         } else {
506             auto flags = static_cast<pa_stream_flags_t>(
507                     PA_STREAM_INTERPOLATE_TIMING |
508                     PA_STREAM_ADJUST_LATENCY     |
509 #ifndef DISABLE_MOBILE_BACK_COMP
510                     PA_STREAM_START_CORKED       |
511 #endif
512                     PA_STREAM_AUTO_TIMING_UPDATE);
513
514             ret = pa_stream_connect_record(__mpStream, NULL, NULL, flags);
515         }
516         if (ret != 0) {
517 //LCOV_EXCL_START
518             err = pa_context_errno(__mpContext);
519             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION,
520                                    "Failed pa_stream_connect() : err[%d]", err);
521 //LCOV_EXCL_STOP
522         }
523
524         AUDIO_IO_LOGD("pa_stream[%p] now wait for stream ready...", __mpStream);
525
526         while (true) {
527             pa_stream_state_t state = pa_stream_get_state(__mpStream);
528             AUDIO_IO_LOGD("pa_stream[%p] current state[%d]", __mpStream, state);
529
530             if (state == PA_STREAM_READY)
531                 break;
532
533             if (!PA_STREAM_IS_GOOD(state)) {
534                 err = pa_context_errno(__mpContext);
535                 if (err == PA_ERR_ACCESS)
536                     THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_DEVICE_POLICY_RESTRICTION,
537                                            "pa_stream's state is not good : err[%d]", err);
538                 else
539                     THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION,   //LCOV_EXCL_LINE
540                                            "pa_stream's state is not good : err[%d]", err); //LCOV_EXCL_LINE
541             }
542
543             /* Wait until the stream is ready */
544             AUDIO_IO_LOGD("pa_mainloop[%p] signal wait...", __mpMainloop);
545             pa_threaded_mainloop_wait(__mpMainloop);
546             AUDIO_IO_LOGD("pa_mainloop[%p] signal wait done", __mpMainloop);
547         }
548         pa_threaded_mainloop_accept(__mpMainloop);
549
550         // __mIsInit = true;  // Moved to __streamStateChangeCb()
551     } catch (const CAudioError& e) {
552 //LCOV_EXCL_START
553         resetStreamCallbacks();
554         resetInternalObjects();
555         throw;
556 //LCOV_EXCL_END
557     }
558
559     AUDIO_IO_LOGD("Done : pa_context[%p], pa_mainloop[%p], pa_stream[%p]", __mpContext, __mpMainloop, __mpStream);
560 }
561
562 void CPulseAudioClient::finalize() {
563     AUDIO_IO_LOGD("pa_context[%p], pa_mainloop[%p], pa_stream[%p]", __mpContext, __mpMainloop, __mpStream);
564
565     if (!__mpMainloop)
566         return;
567
568     bool is_in_thread = isInThread();
569     if (!is_in_thread)
570         pa_threaded_mainloop_lock(__mpMainloop);
571
572     __mIsInit = false;
573
574     resetStreamCallbacks();
575
576     if (!is_in_thread)
577         pa_threaded_mainloop_unlock(__mpMainloop);
578
579     /* Wait for drain complete if draining before finalize */
580     if (__mIsDraining) {
581         unsigned int drain_wait_count = 0;
582         while (__mIsDraining && drain_wait_count++ < drain_wait_max_count)
583             usleep(drain_wait_interval);
584         AUDIO_IO_LOGD("wait for drain complete!!!! [%d * %d usec]",
585                       drain_wait_count, drain_wait_interval);
586     }
587
588     resetInternalObjects();
589
590     AUDIO_IO_LOGD("Done");
591 }
592
593 int CPulseAudioClient::read(void* buffer, size_t length) {
594     if (!__mIsInit)
595         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); //LCOV_EXCL_LINE
596
597     checkRunningState();
598
599     if (!buffer)
600         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "invalid parameter : buffer[%p]", buffer); //LCOV_EXCL_LINE
601
602     if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK)
603         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "playback client can't use this function"); //LCOV_EXCL_LINE
604
605     size_t lengthIter = length;
606     int ret = 0;
607
608     __mIsUsedSyncRead = true;
609
610     try {
611         CPulseThreadLocker locker{__mpMainloop};
612
613         while (lengthIter > 0) {
614             size_t l;
615
616             while (!__mpSyncReadDataPtr) {
617                 ret = pa_stream_peek(__mpStream, &__mpSyncReadDataPtr, &__mSyncReadLength);
618                 if (ret != 0)
619                     THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pa_stream_peek() : ret[%d]", ret); //LCOV_EXCL_LINE
620
621                 if (__mSyncReadLength <= 0) {
622 #ifdef _AUDIO_IO_DEBUG_TIMING_
623                     AUDIO_IO_LOGD("readLength(%zu byte) is not valid. wait...", __mSyncReadLength);
624 #endif
625                     pa_threaded_mainloop_wait(__mpMainloop);
626                 } else if (!__mpSyncReadDataPtr) {
627 // LCOV_EXCL_START
628                     // Data peeked, but it doesn't have any data
629                     ret = pa_stream_drop(__mpStream);
630                     if (ret != 0)
631                         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pa_stream_drop() : ret[%d]", ret);
632 //LCOV_EXCL_STOP
633                 } else {
634                     __mSyncReadIndex = 0;
635                 }
636             }
637
638             l = min(__mSyncReadLength, lengthIter);
639
640             // Copy partial pcm data on out parameter
641 #ifdef _AUDIO_IO_DEBUG_TIMING_
642             AUDIO_IO_LOGD("memcpy() that a peeked buffer[%p], index[%zu], length[%zu] on out buffer", (const uint8_t*)(__mpSyncReadDataPtr) + __mSyncReadIndex, __mSyncReadIndex, l);
643 #endif
644             memcpy(buffer, (const uint8_t*)__mpSyncReadDataPtr + __mSyncReadIndex, l);
645
646             // Move next position
647             buffer = (uint8_t*)buffer + l;
648             lengthIter -= l;
649
650             // Adjusts the rest length
651             __mSyncReadIndex  += l;
652             __mSyncReadLength -= l;
653
654             if (__mSyncReadLength == 0) {
655 #ifdef _AUDIO_IO_DEBUG_TIMING_
656                 AUDIO_IO_LOGD("__mSyncReadLength is zero, do drop()");
657 #endif
658                 ret = pa_stream_drop(__mpStream);
659                 if (ret != 0)
660                     THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, //LCOV_EXCL_LINE
661                                            "Failed pa_stream_drop() : ret[%d]", ret);     //LCOV_EXCL_LINE
662
663                 // Reset the internal pointer
664                 __mpSyncReadDataPtr = nullptr;
665                 __mSyncReadLength   = 0;
666                 __mSyncReadIndex    = 0;
667             }
668         }  // End of while (lengthIter > 0)
669
670         __mIsUsedSyncRead = false;
671     } catch (const CAudioError& e) {
672 // LCOV_EXCL_START
673         __mIsUsedSyncRead = false;
674         throw;
675 // LCOV_EXCL_STOP
676     }
677
678     return length;
679 }
680
681 int CPulseAudioClient::peek(const void** buffer, size_t* length) {
682     if (!__mIsInit)
683         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE
684
685     checkRunningState();
686
687     if (!buffer || !length)
688         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT,                   // LCOV_EXCL_LINE
689                                "invalid parameter : buffer[%p], length[%p]", buffer, length); // LCOV_EXCL_LINE
690
691     if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK)
692         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "playback can't use this function"); // LCOV_EXCL_LINE
693
694     CPulseThreadLocker locker{__mpMainloop};
695
696     int ret = pa_stream_peek(__mpStream, buffer, length);
697
698
699 #ifdef _AUDIO_IO_DEBUG_TIMING_
700     AUDIO_IO_LOGD("buffer[%p], length[%zu]", *buffer, *length);
701 #endif
702
703     if (ret < 0)
704         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_peek() : err[%d]", ret); //LCOV_EXCL_LINE
705
706     return ret;
707 }
708
709 int CPulseAudioClient::drop() {
710     if (!__mIsInit)
711         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE
712
713 #ifdef _AUDIO_IO_DEBUG_TIMING_
714     AUDIO_IO_LOGD("");
715 #endif
716
717     checkRunningState();
718
719     if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK)
720         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "playback can't use this function"); // LCOV_EXCL_LINE
721
722     CPulseThreadLocker locker{__mpMainloop};
723
724     int ret = pa_stream_drop(__mpStream);
725     if (ret < 0)
726         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_drop() : err[%d]", ret); //LCOV_EXCL_LINE
727
728     return ret;
729 }
730
731 int CPulseAudioClient::write(const void* data, size_t length) {
732     if (!data)
733         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_ARGUMENT, "invalid parameter"); // LCOV_EXCL_LINE
734
735     if (__mDirection == EStreamDirection::STREAM_DIRECTION_RECORD)
736         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "playback can't use this function"); // LCOV_EXCL_LINE
737
738     int ret = 0;
739
740 #ifdef _AUDIO_IO_DEBUG_TIMING_
741     AUDIO_IO_LOGD("data[%p], length[%zu], First[%d]", data, length, __mIsFirstStream);
742 #endif
743
744     if (!isInThread()) {
745         CPulseThreadLocker locker{__mpMainloop};
746         if (pa_stream_is_corked(__mpStream)) {
747             AUDIO_IO_LOGW("pa_stream[%p] is corked...do uncork here first!!!!", __mpStream);
748             pa_operation* o = pa_stream_cork(__mpStream, 0, NULL, NULL);
749             if (!o)
750                 AUDIO_IO_LOGE("pa_stream[%p] : uncork failed", __mpStream);
751             else
752                 pa_operation_unref(o);
753         }
754
755         ret = pa_stream_write(__mpStream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
756     } else {
757 // LCOV_EXCL_START
758         if (pa_stream_is_corked(__mpStream)) {
759             AUDIO_IO_LOGW("pa_stream[%p] is corked...do uncork here first!!!!", __mpStream);
760             pa_operation* o = pa_stream_cork(__mpStream, 0, NULL, NULL);
761             if (!o)
762                 AUDIO_IO_LOGE("pa_stream[%p] : uncork failed", __mpStream);
763             else
764                 pa_operation_unref(o);
765         }
766
767         if (__mIsFirstStream) {
768             const pa_buffer_attr* attr = pa_stream_get_buffer_attr(__mpStream);
769             uint32_t prebuf = attr->prebuf;
770             // Compensate amount of prebuf in first stream callback when audio-io use prebuf(-1)
771             // Because a stream will never start when an application wrote less than prebuf at first
772             if (length < prebuf)
773                 __dummy_write(__mpStream, prebuf - length);
774
775             __mIsFirstStream = false;
776             AUDIO_IO_LOGW("FIRST STREAM CALLBACK : length[%zu], prebuf[%d], dummy[%zu]",
777                           length, prebuf, (length < prebuf) ? prebuf - length : 0);
778         }
779         ret = pa_stream_write(__mpStream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
780 // LCOV_EXCL_STOP
781     }
782
783     if (ret < 0)
784         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_write() : err[%d]", ret); //LCOV_EXCL_LINE
785
786     return ret;
787 }
788
789 void CPulseAudioClient::cork(bool cork) {
790     AUDIO_IO_LOGD("cork[%d]", cork);
791
792     if (!__mIsInit)
793         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE
794
795     if (isInThread())
796         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This operation is not supported in callback"); // LCOV_EXCL_LINE
797
798     checkRunningState();
799
800     // Set __mIsFirstStream flag when uncork(resume) stream, because prebuf will be enable again
801     if (!cork)
802         __mIsFirstStream = true;
803
804     CPulseThreadLocker locker{__mpMainloop};
805
806     /* FIXME: wait for completion like drain? */
807     pa_operation* o = pa_stream_cork(__mpStream, static_cast<int>(cork), __successStreamCb, this);
808     if (!o) {
809         AUDIO_IO_LOGE("pa_stream[%p] : cork[%d] failed", __mpStream, cork);
810         return;
811     }
812     pa_operation_unref(o);
813     AUDIO_IO_LOGD("cork[%d] done", cork);
814 }
815
816 bool CPulseAudioClient::isCorked() {
817     if (!__mIsInit)
818         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE
819
820     checkRunningState();
821
822     CPulseThreadLocker locker{__mpMainloop};
823
824     int isCorked = pa_stream_is_corked(__mpStream);
825
826 #ifdef _AUDIO_IO_DEBUG_TIMING_
827     AUDIO_IO_LOGD("isCorked[%d]", isCorked);
828 #endif
829     return static_cast<bool>(isCorked);
830 }
831
832 bool CPulseAudioClient::drain() {
833     if (!__mIsInit)
834         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE
835
836     checkRunningState();
837
838     if (isCorked()) {
839         AUDIO_IO_LOGW("Corked...");
840         return true;
841     }
842
843     if (__mIsDraining) {
844         AUDIO_IO_LOGW("already draining...");
845         return true;
846     }
847
848     if (!__mIsStarted) {
849         AUDIO_IO_LOGW("stream not started yet...skip drain");
850         return true;
851     }
852
853     if (!isInThread()) {
854         AUDIO_IO_LOGD("drain");
855         CPulseThreadLocker locker{__mpMainloop};
856
857         pa_operation* o = pa_stream_drain(__mpStream, __successDrainCb, this);
858         __mIsDraining = true;
859         while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
860             pa_threaded_mainloop_wait(__mpMainloop);
861         pa_operation_unref(o);
862
863         AUDIO_IO_LOGD("drain done");
864     } else {
865         AUDIO_IO_LOGD("drain in thread");
866         pa_operation* o = pa_stream_drain(__mpStream, __successDrainCbInThread, this);
867         if (!o) {
868             AUDIO_IO_LOGE("pa_stream[%p] : drain failed", __mpStream);
869             return false;
870         }
871         pa_operation_unref(o);
872         __mIsDraining = true;
873     }
874
875     return true;
876 }
877
878 bool CPulseAudioClient::flush() {
879     if (!__mIsInit)
880         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE
881
882     checkRunningState();
883
884     CPulseThreadLocker locker{__mpMainloop};
885
886     /* FIXME: wait for completion like drain? */
887     pa_operation* o = pa_stream_flush(__mpStream, __successStreamCb, this);
888     if (!o) {
889         AUDIO_IO_LOGE("pa_stream[%p] : flush failed", __mpStream);
890         return false;
891     }
892     pa_operation_unref(o);
893
894     return true;
895 }
896
897 size_t CPulseAudioClient::getWritableSize() {
898     if (!__mIsInit)
899         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE
900
901     checkRunningState();
902
903     if (__mDirection != EStreamDirection::STREAM_DIRECTION_PLAYBACK)
904         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This client is used for Playback"); // LCOV_EXCL_LINE
905
906     CPulseThreadLocker locker{__mpMainloop};
907
908     return pa_stream_writable_size(__mpStream);
909 }
910
911 void CPulseAudioClient::checkRunningState() {
912     if (!__mpContext || !PA_CONTEXT_IS_GOOD(pa_context_get_state(__mpContext)))
913         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_INITIALIZED,                       // LCOV_EXCL_LINE
914                                "The context[%p] is not created or not good state", __mpContext); // LCOV_EXCL_LINE
915
916     if (!__mpStream || !PA_STREAM_IS_GOOD(pa_stream_get_state(__mpStream)))
917         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_INITIALIZED,                     // LCOV_EXCL_LINE
918                                "The stream[%p] is not created or not good state", __mpStream); // LCOV_EXCL_LINE
919
920     if (pa_context_get_state(__mpContext) != PA_CONTEXT_READY || pa_stream_get_state(__mpStream) != PA_STREAM_READY)
921         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_INITIALIZED,                                   // LCOV_EXCL_LINE
922                                "The context[%p] or stream[%p] state is not ready", __mpContext, __mpStream); // LCOV_EXCL_LINE
923
924 #ifdef _AUDIO_IO_DEBUG_TIMING_
925     AUDIO_IO_LOGD("This client is running");
926 #endif
927 }
928
929 bool CPulseAudioClient::isInThread() const {
930     if (!__mIsInit)
931         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "__mIsInit is null");
932
933     if (!__mpMainloop)
934         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "__mpMainloop is null");
935
936     int ret = pa_threaded_mainloop_in_thread(__mpMainloop);
937
938 #ifdef _AUDIO_IO_DEBUG_TIMING_
939     AUDIO_IO_LOGD("isInThread[%d]", ret);
940 #endif
941     return static_cast<bool>(ret);
942 }
943
944 //LCOV_EXCL_START
945 size_t CPulseAudioClient::getReadableSize() {
946     if (!__mIsInit)
947         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized");
948
949     checkRunningState();
950
951     if (__mDirection != EStreamDirection::STREAM_DIRECTION_RECORD)
952         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This client is used for Capture");
953
954     CPulseThreadLocker locker{__mpMainloop};
955
956     return pa_stream_readable_size(__mpStream);
957 }
958
959 size_t CPulseAudioClient::getBufferSize() {
960     if (!__mIsInit)
961         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized");
962
963     checkRunningState();
964
965     size_t buffer_size = 0;
966     CPulseThreadLocker locker{__mpMainloop};
967
968     const pa_buffer_attr* attr = pa_stream_get_buffer_attr(__mpStream);
969     if (!attr)
970         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION,
971                                "Failed pa_stream_get_buffer_attr() : err[%d]", pa_context_errno(__mpContext));
972
973     if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
974         buffer_size = attr->tlength;
975         AUDIO_IO_LOGD("PLAYBACK buffer size[%zu]", buffer_size);
976     } else {
977         buffer_size = attr->fragsize;
978         AUDIO_IO_LOGD("RECORD buffer size[%zu]", buffer_size);
979     }
980
981     return buffer_size;
982 }
983
984 pa_usec_t CPulseAudioClient::getLatency() {
985     if (!__mIsInit)
986         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized");
987
988     checkRunningState();
989
990     pa_usec_t latency = 0;
991     int negative = 0;
992
993     if (!isInThread()) {
994         if (pa_stream_get_latency(__mpStream, &latency, &negative) < 0) {
995             int _err = pa_context_errno(__mpContext);
996             if (_err != PA_ERR_NODATA)
997                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION,
998                                        "Failed pa_stream_get_latency() : err[%d]", _err);
999         }
1000         return negative ? 0 : latency;
1001     }
1002
1003     CPulseThreadLocker locker{__mpMainloop};
1004
1005     while (pa_stream_get_latency(__mpStream, &latency, &negative) < 0) {
1006         int _err = pa_context_errno(__mpContext);
1007         if (_err != PA_ERR_NODATA)
1008             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION,
1009                                    "Failed pa_stream_get_latency() : err[%d]", _err);
1010         /* Wait until latency data is available again */
1011         pa_threaded_mainloop_wait(__mpMainloop);
1012     }
1013
1014     return negative ? 0 : latency;
1015 }
1016
1017 pa_usec_t CPulseAudioClient::getFinalLatency() {
1018     if (!__mIsInit)
1019         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized");
1020
1021     checkRunningState();
1022
1023     pa_usec_t latency = 0;
1024
1025     CPulseThreadLocker locker{__mpMainloop};
1026
1027     uint32_t ver = pa_context_get_server_protocol_version(__mpContext);
1028     if (ver < 13)
1029         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_SUPPORTED, "This version(ver.%d) is not supported", ver);
1030
1031     const pa_buffer_attr* buffer_attr = pa_stream_get_buffer_attr(__mpStream);
1032     const pa_sample_spec* sample_spec = pa_stream_get_sample_spec(__mpStream);
1033     const pa_timing_info* timing_info = pa_stream_get_timing_info(__mpStream);
1034
1035     if (!buffer_attr || !sample_spec || !timing_info)
1036         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_OUT_OF_MEMORY,
1037                 "Failed to get buffer_attr[%p] or sample_spec[%p] or timing_info[%p] from a pa_stream",
1038                 buffer_attr, sample_spec, timing_info);
1039
1040     if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
1041         latency = (pa_bytes_to_usec(buffer_attr->tlength, sample_spec) + timing_info->configured_sink_usec);
1042         AUDIO_IO_LOGD("FINAL PLAYBACK LATENCY[%" PRIu64 "]", latency);
1043     } else {
1044         latency = (pa_bytes_to_usec(buffer_attr->fragsize, sample_spec) + timing_info->configured_source_usec);
1045         AUDIO_IO_LOGD("FINAL RECORD LATENCY[%" PRIu64 "]", latency);
1046     }
1047
1048     return latency;
1049 }
1050 //LCOV_EXCL_STOP
1051
1052 void CPulseAudioClient::applyRecordVolume(double volume) {
1053
1054     if (!__mIsInit)
1055         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized");
1056
1057     checkRunningState();
1058
1059     if (__mDirection != EStreamDirection::STREAM_DIRECTION_RECORD)
1060         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "The Playback client can't use this function"); //LCOV_EXCL_LINE
1061
1062     CPulseThreadLocker locker{__mpMainloop};
1063
1064     pa_cvolume cv = { 0, };
1065     pa_volume_t v = PA_VOLUME_NORM * volume;
1066
1067     pa_cvolume_set(&cv, __mSpec.getChannelMap().channels, v);
1068
1069     pa_operation_unref(pa_context_set_source_output_volume(__mpContext,
1070             pa_stream_get_index(__mpStream), &cv, __successVolumeCb, NULL));
1071 }