d143d9445d52582c0fca2475fb5a5a2c1edfcaae
[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 ns_method = __mSpec.getAudioInfo().getEffectNoiseSuppressionMethod();
404             auto [ method_reference, device_id ] = __mSpec.getAudioInfo().getEffectMethodWithReference();
405             std::string agc_method = __mSpec.getAudioInfo().getEffectAutomaticGainControlMethod();
406
407             std::string method_all = ns_method + method_reference + agc_method;
408
409             if (!method_all.empty()) {
410                 pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_PREPROCESSOR_METHOD, "%s", method_all.c_str());
411                 pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_ECHO_CANCEL_REFERENCE_DEVICE, "%d", device_id);
412
413                 AUDIO_IO_LOGD("preprocess will be set. method[%s], device_id[%d]", method_all.c_str(), device_id);
414             }
415         }
416
417         // Adds latency on proplist for delivery to PULSEAUDIO
418         AUDIO_IO_LOGD("LATENCY : %s[%d]", __mSpec.getStreamLatencyToString(), static_cast<int>(__mSpec.getStreamLatency()));
419         pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_TIZEN_AUDIO_LATENCY, "%s", __mSpec.getStreamLatencyToString());
420
421         // Allocates PA mainloop
422         __mpMainloop = pa_threaded_mainloop_new();
423         if (!__mpMainloop)
424             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_threaded_mainloop_new()"); //LCOV_EXCL_LINE
425
426         // Allocates PA context
427         __mpContext = pa_context_new(pa_threaded_mainloop_get_api(__mpMainloop), CLIENT_NAME);
428         if (!__mpContext)
429             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_context_new()"); //LCOV_EXCL_LINE
430
431         // Sets context state changed callback
432         pa_context_set_state_callback(__mpContext, __contextStateChangeCb, this);
433
434         // Connects this client with PA server
435         if (pa_context_connect(__mpContext, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0)
436             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_context_connect()"); //LCOV_EXCL_LINE
437
438         // LOCK for synchronous connection
439         CPulseThreadLocker locker{__mpMainloop};
440
441         // Start mainloop
442         if (pa_threaded_mainloop_start(__mpMainloop) < 0) {
443 //LCOV_EXCL_START
444             THROW_ERROR_MSG(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_threaded_mainloop_start()");
445 //LCOV_EXCL_STOP
446         }
447
448         // Connection process is asynchronously
449         // So, this function will be waited when occurred context state change event
450         // If I got a signal, do next processing
451         while (true) {
452             pa_context_state_t state;
453             state = pa_context_get_state(__mpContext);
454
455             if (state == PA_CONTEXT_READY)
456                 break;
457
458             if (!PA_CONTEXT_IS_GOOD(state)) {
459 //LCOV_EXCL_START
460                 err = pa_context_errno(__mpContext);
461                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION,
462                                        "pa_context's state is not good : err[%d]", err);
463 //LCOV_EXCL_STOP
464             }
465
466             /* Wait until the context is ready */
467             pa_threaded_mainloop_wait(__mpMainloop);
468         }
469
470         AUDIO_IO_LOGD("pa_context[%p] now ready", __mpContext);
471
472         // Allocates PA stream
473         pa_sample_spec ss   = __mSpec.getSampleSpec();
474         pa_channel_map map  = __mSpec.getChannelMap();
475
476         __mpStream = pa_stream_new_with_proplist(__mpContext, __mSpec.getStreamName(), &ss, &map, __mpPropList);
477         if (!__mpStream) {
478 //LCOV_EXCL_START
479             THROW_ERROR_MSG(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_new_with_proplist()");
480 //LCOV_EXCL_STOP
481         }
482
483         // Sets stream callbacks
484         pa_stream_set_state_callback(__mpStream, __streamStateChangeCb, this);
485         pa_stream_set_read_callback(__mpStream, __streamCaptureCb, this);
486         pa_stream_set_write_callback(__mpStream, __streamPlaybackCb, this);
487         pa_stream_set_latency_update_callback(__mpStream, __streamLatencyUpdateCb, this);
488         pa_stream_set_event_callback(__mpStream, __streamEventCb, this);
489         if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
490             pa_stream_set_started_callback(__mpStream, __streamStartedCb, this);
491             pa_stream_set_underflow_callback(__mpStream, __streamUnderflowCb, this);
492             pa_stream_set_overflow_callback(__mpStream, __streamOverflowCb, this);
493         }
494
495         // Connect stream with PA Server
496
497         if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
498             auto flags = static_cast<pa_stream_flags_t>(
499                     PA_STREAM_INTERPOLATE_TIMING |
500                     PA_STREAM_ADJUST_LATENCY     |
501 #ifndef DISABLE_MOBILE_BACK_COMP
502                     PA_STREAM_START_CORKED       |
503 #endif
504                     PA_STREAM_AUTO_TIMING_UPDATE);
505
506             ret = pa_stream_connect_playback(__mpStream, NULL, NULL, flags, NULL, NULL);
507         } else {
508             auto flags = static_cast<pa_stream_flags_t>(
509                     PA_STREAM_INTERPOLATE_TIMING |
510                     PA_STREAM_ADJUST_LATENCY     |
511 #ifndef DISABLE_MOBILE_BACK_COMP
512                     PA_STREAM_START_CORKED       |
513 #endif
514                     PA_STREAM_AUTO_TIMING_UPDATE);
515
516             ret = pa_stream_connect_record(__mpStream, NULL, NULL, flags);
517         }
518         if (ret != 0) {
519 //LCOV_EXCL_START
520             err = pa_context_errno(__mpContext);
521             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION,
522                                    "Failed pa_stream_connect() : err[%d]", err);
523 //LCOV_EXCL_STOP
524         }
525
526         AUDIO_IO_LOGD("pa_stream[%p] now wait for stream ready...", __mpStream);
527
528         while (true) {
529             pa_stream_state_t state = pa_stream_get_state(__mpStream);
530             AUDIO_IO_LOGD("pa_stream[%p] current state[%d]", __mpStream, state);
531
532             if (state == PA_STREAM_READY)
533                 break;
534
535             if (!PA_STREAM_IS_GOOD(state)) {
536                 err = pa_context_errno(__mpContext);
537                 if (err == PA_ERR_ACCESS)
538                     THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_DEVICE_POLICY_RESTRICTION,
539                                            "pa_stream's state is not good : err[%d]", err);
540                 else
541                     THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION,   //LCOV_EXCL_LINE
542                                            "pa_stream's state is not good : err[%d]", err); //LCOV_EXCL_LINE
543             }
544
545             /* Wait until the stream is ready */
546             AUDIO_IO_LOGD("pa_mainloop[%p] signal wait...", __mpMainloop);
547             pa_threaded_mainloop_wait(__mpMainloop);
548             AUDIO_IO_LOGD("pa_mainloop[%p] signal wait done", __mpMainloop);
549         }
550         pa_threaded_mainloop_accept(__mpMainloop);
551
552         // __mIsInit = true;  // Moved to __streamStateChangeCb()
553     } catch (const CAudioError& e) {
554 //LCOV_EXCL_START
555         resetStreamCallbacks();
556         resetInternalObjects();
557         throw;
558 //LCOV_EXCL_END
559     }
560
561     AUDIO_IO_LOGD("Done : pa_context[%p], pa_mainloop[%p], pa_stream[%p]", __mpContext, __mpMainloop, __mpStream);
562 }
563
564 void CPulseAudioClient::finalize() {
565     AUDIO_IO_LOGD("pa_context[%p], pa_mainloop[%p], pa_stream[%p]", __mpContext, __mpMainloop, __mpStream);
566
567     if (!__mpMainloop)
568         return;
569
570     bool is_in_thread = isInThread();
571     if (!is_in_thread)
572         pa_threaded_mainloop_lock(__mpMainloop);
573
574     __mIsInit = false;
575
576     resetStreamCallbacks();
577
578     if (!is_in_thread)
579         pa_threaded_mainloop_unlock(__mpMainloop);
580
581     /* Wait for drain complete if draining before finalize */
582     if (__mIsDraining) {
583         unsigned int drain_wait_count = 0;
584         while (__mIsDraining && drain_wait_count++ < drain_wait_max_count)
585             usleep(drain_wait_interval);
586         AUDIO_IO_LOGD("wait for drain complete!!!! [%d * %d usec]",
587                       drain_wait_count, drain_wait_interval);
588     }
589
590     resetInternalObjects();
591
592     AUDIO_IO_LOGD("Done");
593 }
594
595 int CPulseAudioClient::read(void* buffer, size_t length) {
596     if (!__mIsInit)
597         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); //LCOV_EXCL_LINE
598
599     checkRunningState();
600
601     if (!buffer)
602         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "invalid parameter : buffer[%p]", buffer); //LCOV_EXCL_LINE
603
604     if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK)
605         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "playback client can't use this function"); //LCOV_EXCL_LINE
606
607     size_t lengthIter = length;
608     int ret = 0;
609
610     __mIsUsedSyncRead = true;
611
612     try {
613         CPulseThreadLocker locker{__mpMainloop};
614
615         while (lengthIter > 0) {
616             size_t l;
617
618             while (!__mpSyncReadDataPtr) {
619                 ret = pa_stream_peek(__mpStream, &__mpSyncReadDataPtr, &__mSyncReadLength);
620                 if (ret != 0)
621                     THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pa_stream_peek() : ret[%d]", ret); //LCOV_EXCL_LINE
622
623                 if (__mSyncReadLength <= 0) {
624 #ifdef _AUDIO_IO_DEBUG_TIMING_
625                     AUDIO_IO_LOGD("readLength(%zu byte) is not valid. wait...", __mSyncReadLength);
626 #endif
627                     pa_threaded_mainloop_wait(__mpMainloop);
628                 } else if (!__mpSyncReadDataPtr) {
629 // LCOV_EXCL_START
630                     // Data peeked, but it doesn't have any data
631                     ret = pa_stream_drop(__mpStream);
632                     if (ret != 0)
633                         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pa_stream_drop() : ret[%d]", ret);
634 //LCOV_EXCL_STOP
635                 } else {
636                     __mSyncReadIndex = 0;
637                 }
638             }
639
640             l = min(__mSyncReadLength, lengthIter);
641
642             // Copy partial pcm data on out parameter
643 #ifdef _AUDIO_IO_DEBUG_TIMING_
644             AUDIO_IO_LOGD("memcpy() that a peeked buffer[%p], index[%zu], length[%zu] on out buffer", (const uint8_t*)(__mpSyncReadDataPtr) + __mSyncReadIndex, __mSyncReadIndex, l);
645 #endif
646             memcpy(buffer, (const uint8_t*)__mpSyncReadDataPtr + __mSyncReadIndex, l);
647
648             // Move next position
649             buffer = (uint8_t*)buffer + l;
650             lengthIter -= l;
651
652             // Adjusts the rest length
653             __mSyncReadIndex  += l;
654             __mSyncReadLength -= l;
655
656             if (__mSyncReadLength == 0) {
657 #ifdef _AUDIO_IO_DEBUG_TIMING_
658                 AUDIO_IO_LOGD("__mSyncReadLength is zero, do drop()");
659 #endif
660                 ret = pa_stream_drop(__mpStream);
661                 if (ret != 0)
662                     THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, //LCOV_EXCL_LINE
663                                            "Failed pa_stream_drop() : ret[%d]", ret);     //LCOV_EXCL_LINE
664
665                 // Reset the internal pointer
666                 __mpSyncReadDataPtr = nullptr;
667                 __mSyncReadLength   = 0;
668                 __mSyncReadIndex    = 0;
669             }
670         }  // End of while (lengthIter > 0)
671
672         __mIsUsedSyncRead = false;
673     } catch (const CAudioError& e) {
674 // LCOV_EXCL_START
675         __mIsUsedSyncRead = false;
676         throw;
677 // LCOV_EXCL_STOP
678     }
679
680     return length;
681 }
682
683 int CPulseAudioClient::peek(const void** buffer, size_t* length) {
684     if (!__mIsInit)
685         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE
686
687     checkRunningState();
688
689     if (!buffer || !length)
690         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT,                   // LCOV_EXCL_LINE
691                                "invalid parameter : buffer[%p], length[%p]", buffer, length); // LCOV_EXCL_LINE
692
693     if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK)
694         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "playback can't use this function"); // LCOV_EXCL_LINE
695
696     CPulseThreadLocker locker{__mpMainloop};
697
698     int ret = pa_stream_peek(__mpStream, buffer, length);
699
700
701 #ifdef _AUDIO_IO_DEBUG_TIMING_
702     AUDIO_IO_LOGD("buffer[%p], length[%zu]", *buffer, *length);
703 #endif
704
705     if (ret < 0)
706         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_peek() : err[%d]", ret); //LCOV_EXCL_LINE
707
708     return ret;
709 }
710
711 int CPulseAudioClient::drop() {
712     if (!__mIsInit)
713         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE
714
715 #ifdef _AUDIO_IO_DEBUG_TIMING_
716     AUDIO_IO_LOGD("");
717 #endif
718
719     checkRunningState();
720
721     if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK)
722         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "playback can't use this function"); // LCOV_EXCL_LINE
723
724     CPulseThreadLocker locker{__mpMainloop};
725
726     int ret = pa_stream_drop(__mpStream);
727     if (ret < 0)
728         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_drop() : err[%d]", ret); //LCOV_EXCL_LINE
729
730     return ret;
731 }
732
733 int CPulseAudioClient::write(const void* data, size_t length) {
734     if (!data)
735         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_ARGUMENT, "invalid parameter"); // LCOV_EXCL_LINE
736
737     if (__mDirection == EStreamDirection::STREAM_DIRECTION_RECORD)
738         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "playback can't use this function"); // LCOV_EXCL_LINE
739
740     int ret = 0;
741
742 #ifdef _AUDIO_IO_DEBUG_TIMING_
743     AUDIO_IO_LOGD("data[%p], length[%zu], First[%d]", data, length, __mIsFirstStream);
744 #endif
745
746     if (!isInThread()) {
747         CPulseThreadLocker locker{__mpMainloop};
748         if (pa_stream_is_corked(__mpStream)) {
749             AUDIO_IO_LOGW("pa_stream[%p] is corked...do uncork here first!!!!", __mpStream);
750             pa_operation* o = pa_stream_cork(__mpStream, 0, NULL, NULL);
751             if (!o)
752                 AUDIO_IO_LOGE("pa_stream[%p] : uncork failed", __mpStream);
753             else
754                 pa_operation_unref(o);
755         }
756
757         ret = pa_stream_write(__mpStream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
758     } else {
759 // LCOV_EXCL_START
760         if (pa_stream_is_corked(__mpStream)) {
761             AUDIO_IO_LOGW("pa_stream[%p] is corked...do uncork here first!!!!", __mpStream);
762             pa_operation* o = pa_stream_cork(__mpStream, 0, NULL, NULL);
763             if (!o)
764                 AUDIO_IO_LOGE("pa_stream[%p] : uncork failed", __mpStream);
765             else
766                 pa_operation_unref(o);
767         }
768
769         if (__mIsFirstStream) {
770             const pa_buffer_attr* attr = pa_stream_get_buffer_attr(__mpStream);
771             uint32_t prebuf = attr->prebuf;
772             // Compensate amount of prebuf in first stream callback when audio-io use prebuf(-1)
773             // Because a stream will never start when an application wrote less than prebuf at first
774             if (length < prebuf)
775                 __dummy_write(__mpStream, prebuf - length);
776
777             __mIsFirstStream = false;
778             AUDIO_IO_LOGW("FIRST STREAM CALLBACK : length[%zu], prebuf[%d], dummy[%zu]",
779                           length, prebuf, (length < prebuf) ? prebuf - length : 0);
780         }
781         ret = pa_stream_write(__mpStream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
782 // LCOV_EXCL_STOP
783     }
784
785     if (ret < 0)
786         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_write() : err[%d]", ret); //LCOV_EXCL_LINE
787
788     return ret;
789 }
790
791 void CPulseAudioClient::cork(bool cork) {
792     AUDIO_IO_LOGD("cork[%d]", cork);
793
794     if (!__mIsInit)
795         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE
796
797     if (isInThread())
798         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This operation is not supported in callback"); // LCOV_EXCL_LINE
799
800     checkRunningState();
801
802     // Set __mIsFirstStream flag when uncork(resume) stream, because prebuf will be enable again
803     if (!cork)
804         __mIsFirstStream = true;
805
806     CPulseThreadLocker locker{__mpMainloop};
807
808     /* FIXME: wait for completion like drain? */
809     pa_operation* o = pa_stream_cork(__mpStream, static_cast<int>(cork), __successStreamCb, this);
810     if (!o) {
811         AUDIO_IO_LOGE("pa_stream[%p] : cork[%d] failed", __mpStream, cork);
812         return;
813     }
814     pa_operation_unref(o);
815     AUDIO_IO_LOGD("cork[%d] done", cork);
816 }
817
818 bool CPulseAudioClient::isCorked() {
819     if (!__mIsInit)
820         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE
821
822     checkRunningState();
823
824     CPulseThreadLocker locker{__mpMainloop};
825
826     int isCorked = pa_stream_is_corked(__mpStream);
827
828 #ifdef _AUDIO_IO_DEBUG_TIMING_
829     AUDIO_IO_LOGD("isCorked[%d]", isCorked);
830 #endif
831     return static_cast<bool>(isCorked);
832 }
833
834 bool CPulseAudioClient::drain() {
835     if (!__mIsInit)
836         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE
837
838     checkRunningState();
839
840     if (isCorked()) {
841         AUDIO_IO_LOGW("Corked...");
842         return true;
843     }
844
845     if (__mIsDraining) {
846         AUDIO_IO_LOGW("already draining...");
847         return true;
848     }
849
850     if (!__mIsStarted) {
851         AUDIO_IO_LOGW("stream not started yet...skip drain");
852         return true;
853     }
854
855     if (!isInThread()) {
856         AUDIO_IO_LOGD("drain");
857         CPulseThreadLocker locker{__mpMainloop};
858
859         pa_operation* o = pa_stream_drain(__mpStream, __successDrainCb, this);
860         __mIsDraining = true;
861         while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
862             pa_threaded_mainloop_wait(__mpMainloop);
863         pa_operation_unref(o);
864
865         AUDIO_IO_LOGD("drain done");
866     } else {
867         AUDIO_IO_LOGD("drain in thread");
868         pa_operation* o = pa_stream_drain(__mpStream, __successDrainCbInThread, this);
869         if (!o) {
870             AUDIO_IO_LOGE("pa_stream[%p] : drain failed", __mpStream);
871             return false;
872         }
873         pa_operation_unref(o);
874         __mIsDraining = true;
875     }
876
877     return true;
878 }
879
880 bool CPulseAudioClient::flush() {
881     if (!__mIsInit)
882         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE
883
884     checkRunningState();
885
886     CPulseThreadLocker locker{__mpMainloop};
887
888     /* FIXME: wait for completion like drain? */
889     pa_operation* o = pa_stream_flush(__mpStream, __successStreamCb, this);
890     if (!o) {
891         AUDIO_IO_LOGE("pa_stream[%p] : flush failed", __mpStream);
892         return false;
893     }
894     pa_operation_unref(o);
895
896     return true;
897 }
898
899 size_t CPulseAudioClient::getWritableSize() {
900     if (!__mIsInit)
901         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized"); // LCOV_EXCL_LINE
902
903     checkRunningState();
904
905     if (__mDirection != EStreamDirection::STREAM_DIRECTION_PLAYBACK)
906         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This client is used for Playback"); // LCOV_EXCL_LINE
907
908     CPulseThreadLocker locker{__mpMainloop};
909
910     return pa_stream_writable_size(__mpStream);
911 }
912
913 void CPulseAudioClient::checkRunningState() {
914     if (!__mpContext || !PA_CONTEXT_IS_GOOD(pa_context_get_state(__mpContext)))
915         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_INITIALIZED,                       // LCOV_EXCL_LINE
916                                "The context[%p] is not created or not good state", __mpContext); // LCOV_EXCL_LINE
917
918     if (!__mpStream || !PA_STREAM_IS_GOOD(pa_stream_get_state(__mpStream)))
919         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_INITIALIZED,                     // LCOV_EXCL_LINE
920                                "The stream[%p] is not created or not good state", __mpStream); // LCOV_EXCL_LINE
921
922     if (pa_context_get_state(__mpContext) != PA_CONTEXT_READY || pa_stream_get_state(__mpStream) != PA_STREAM_READY)
923         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_INITIALIZED,                                   // LCOV_EXCL_LINE
924                                "The context[%p] or stream[%p] state is not ready", __mpContext, __mpStream); // LCOV_EXCL_LINE
925
926 #ifdef _AUDIO_IO_DEBUG_TIMING_
927     AUDIO_IO_LOGD("This client is running");
928 #endif
929 }
930
931 bool CPulseAudioClient::isInThread() const {
932     if (!__mIsInit)
933         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "__mIsInit is null");
934
935     if (!__mpMainloop)
936         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "__mpMainloop is null");
937
938     int ret = pa_threaded_mainloop_in_thread(__mpMainloop);
939
940 #ifdef _AUDIO_IO_DEBUG_TIMING_
941     AUDIO_IO_LOGD("isInThread[%d]", ret);
942 #endif
943     return static_cast<bool>(ret);
944 }
945
946 //LCOV_EXCL_START
947 size_t CPulseAudioClient::getReadableSize() {
948     if (!__mIsInit)
949         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized");
950
951     checkRunningState();
952
953     if (__mDirection != EStreamDirection::STREAM_DIRECTION_RECORD)
954         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This client is used for Capture");
955
956     CPulseThreadLocker locker{__mpMainloop};
957
958     return pa_stream_readable_size(__mpStream);
959 }
960
961 size_t CPulseAudioClient::getBufferSize() {
962     if (!__mIsInit)
963         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized");
964
965     checkRunningState();
966
967     size_t buffer_size = 0;
968     CPulseThreadLocker locker{__mpMainloop};
969
970     const pa_buffer_attr* attr = pa_stream_get_buffer_attr(__mpStream);
971     if (!attr)
972         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION,
973                                "Failed pa_stream_get_buffer_attr() : err[%d]", pa_context_errno(__mpContext));
974
975     if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
976         buffer_size = attr->tlength;
977         AUDIO_IO_LOGD("PLAYBACK buffer size[%zu]", buffer_size);
978     } else {
979         buffer_size = attr->fragsize;
980         AUDIO_IO_LOGD("RECORD buffer size[%zu]", buffer_size);
981     }
982
983     return buffer_size;
984 }
985
986 pa_usec_t CPulseAudioClient::getLatency() {
987     if (!__mIsInit)
988         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized");
989
990     checkRunningState();
991
992     pa_usec_t latency = 0;
993     int negative = 0;
994
995     if (!isInThread()) {
996         if (pa_stream_get_latency(__mpStream, &latency, &negative) < 0) {
997             int _err = pa_context_errno(__mpContext);
998             if (_err != PA_ERR_NODATA)
999                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION,
1000                                        "Failed pa_stream_get_latency() : err[%d]", _err);
1001         }
1002         return negative ? 0 : latency;
1003     }
1004
1005     CPulseThreadLocker locker{__mpMainloop};
1006
1007     while (pa_stream_get_latency(__mpStream, &latency, &negative) < 0) {
1008         int _err = pa_context_errno(__mpContext);
1009         if (_err != PA_ERR_NODATA)
1010             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION,
1011                                    "Failed pa_stream_get_latency() : err[%d]", _err);
1012         /* Wait until latency data is available again */
1013         pa_threaded_mainloop_wait(__mpMainloop);
1014     }
1015
1016     return negative ? 0 : latency;
1017 }
1018
1019 pa_usec_t CPulseAudioClient::getFinalLatency() {
1020     if (!__mIsInit)
1021         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized");
1022
1023     checkRunningState();
1024
1025     pa_usec_t latency = 0;
1026
1027     CPulseThreadLocker locker{__mpMainloop};
1028
1029     uint32_t ver = pa_context_get_server_protocol_version(__mpContext);
1030     if (ver < 13)
1031         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_SUPPORTED, "This version(ver.%d) is not supported", ver);
1032
1033     const pa_buffer_attr* buffer_attr = pa_stream_get_buffer_attr(__mpStream);
1034     const pa_sample_spec* sample_spec = pa_stream_get_sample_spec(__mpStream);
1035     const pa_timing_info* timing_info = pa_stream_get_timing_info(__mpStream);
1036
1037     if (!buffer_attr || !sample_spec || !timing_info)
1038         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_OUT_OF_MEMORY,
1039                 "Failed to get buffer_attr[%p] or sample_spec[%p] or timing_info[%p] from a pa_stream",
1040                 buffer_attr, sample_spec, timing_info);
1041
1042     if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
1043         latency = (pa_bytes_to_usec(buffer_attr->tlength, sample_spec) + timing_info->configured_sink_usec);
1044         AUDIO_IO_LOGD("FINAL PLAYBACK LATENCY[%" PRIu64 "]", latency);
1045     } else {
1046         latency = (pa_bytes_to_usec(buffer_attr->fragsize, sample_spec) + timing_info->configured_source_usec);
1047         AUDIO_IO_LOGD("FINAL RECORD LATENCY[%" PRIu64 "]", latency);
1048     }
1049
1050     return latency;
1051 }
1052 //LCOV_EXCL_STOP
1053
1054 void CPulseAudioClient::applyRecordVolume(double volume) {
1055
1056     if (!__mIsInit)
1057         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "CPulseAudioClient is not initialized");
1058
1059     checkRunningState();
1060
1061     if (__mDirection != EStreamDirection::STREAM_DIRECTION_RECORD)
1062         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "The Playback client can't use this function"); //LCOV_EXCL_LINE
1063
1064     CPulseThreadLocker locker{__mpMainloop};
1065
1066     pa_cvolume cv = { 0, };
1067     pa_volume_t v = PA_VOLUME_NORM * volume;
1068
1069     pa_cvolume_set(&cv, __mSpec.getChannelMap().channels, v);
1070
1071     pa_operation_unref(pa_context_set_source_output_volume(__mpContext,
1072             pa_stream_get_index(__mpStream), &cv, __successVolumeCb, NULL));
1073 }