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