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