733fe0551d59cb7ca60e9620435da79aaa1d93d6
[platform/core/api/audio-io.git] / src / cpp / CPulseAudioClient.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17
18 #include <mm.h>
19 #include "CAudioIODef.h"
20
21
22 using namespace std;
23 using namespace tizen_media_audio;
24
25
26 /**
27  * class CPulseAudioClient
28  */
29 const char* CPulseAudioClient::CLIENT_NAME = "AUDIO_IO_PA_CLIENT";
30
31 CPulseAudioClient::CPulseAudioClient(EStreamDirection      direction,
32                                      CPulseStreamSpec&     spec,
33                                      IPulseStreamListener* listener) :
34     __mDirection(direction),
35     __mSpec(spec),
36     __mpListener(listener),
37     __mpMainloop(NULL),
38     __mpContext(NULL),
39     __mpStream(NULL),
40     __mpPropList(NULL),
41     __mIsInit(false),
42     __mIsOperationSuccess(false) {
43 }
44
45 CPulseAudioClient::~CPulseAudioClient() {
46     finalize();
47 }
48
49 void CPulseAudioClient::__contextStateChangeCb(pa_context* c, void* user_data) {
50     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
51     assert(pClient);
52     assert(c);
53
54     switch (pa_context_get_state(c)) {
55     case PA_CONTEXT_READY:
56         AUDIO_IO_LOGD("The context is ready!");
57     case PA_CONTEXT_FAILED:
58     case PA_CONTEXT_TERMINATED:
59         pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
60         break;
61
62     case PA_CONTEXT_UNCONNECTED:
63     case PA_CONTEXT_CONNECTING:
64     case PA_CONTEXT_AUTHORIZING:
65     case PA_CONTEXT_SETTING_NAME:
66         break;
67     }
68 }
69
70 void CPulseAudioClient::__successContextCb(pa_context* c, int success, void* user_data) {
71     AUDIO_IO_LOGD("pa_context[%p], success[%d], user_data[%p]", c, success, user_data);
72     assert(c);
73     assert(user_data);
74
75     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
76     pClient->__mIsOperationSuccess = static_cast<bool>(success);
77
78     pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
79 }
80
81 void CPulseAudioClient::__streamStateChangeCb(pa_stream* s, void* user_data) {
82     assert(s);
83     assert(user_data);
84
85     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
86
87     switch (pa_stream_get_state(s)) {
88     case PA_STREAM_READY:
89         AUDIO_IO_LOGD("The stream is ready!");
90         pClient->__mpListener->onStateChanged(CAudioInfo::AUDIO_IO_STATE_RUNNING);
91     case PA_STREAM_FAILED:
92     case PA_STREAM_TERMINATED:
93         pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
94         break;
95
96     case PA_STREAM_UNCONNECTED:
97         break;
98     case PA_STREAM_CREATING:
99         break;
100     }
101 }
102
103 void CPulseAudioClient::__streamCaptureCb(pa_stream* s, size_t length, void* user_data) {
104     assert(s);
105     assert(user_data);
106
107     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
108     assert(pClient->__mpListener);
109
110     pClient->__mpListener->onStream(pClient, length);
111 }
112
113 void CPulseAudioClient::__streamPlaybackCb(pa_stream* s, size_t length, void* user_data) {
114     //AUDIO_IO_LOGD("__streamPlaybackCb()");
115     assert(s);
116     assert(user_data);
117
118     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
119     assert(pClient->__mpListener);
120
121     if (pClient->__mIsInit == false) {
122         AUDIO_IO_LOGD("Occurred this listener when an out stream is on the way to create - Dummy write[length:%d]", length);
123
124         char* dummy = new char[length];
125         memset(dummy, 0, length);
126         pa_stream_write(s, dummy, length, NULL, 0LL, PA_SEEK_RELATIVE);
127         delete [] dummy;
128
129         return;
130     }
131
132     pClient->__mpListener->onStream(pClient, length);
133 }
134
135 void CPulseAudioClient::__streamLatencyUpdateCb(pa_stream* s, void* user_data) {
136     assert(s);
137     assert(user_data);
138
139     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
140
141     pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
142 }
143
144 void CPulseAudioClient::__successStreamCb(pa_stream* s, int success, void* user_data) {
145     AUDIO_IO_LOGD("pa_stream[%p], success[%d], user_data[%p]", s, success, user_data);
146     assert(s);
147     assert(user_data);
148
149     CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
150     pClient->__mIsOperationSuccess = static_cast<bool>(success);
151
152     pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
153 }
154
155 void CPulseAudioClient::initialize() throw (CAudioError) {
156     AUDIO_IO_LOGD("");
157     if (__mIsInit == true) {
158         return;
159     }
160
161     int ret = 0;
162     int err = 0;
163
164     try {
165         // Allocates PA proplist
166         __mpPropList = pa_proplist_new();
167         if (__mpPropList == NULL) {
168             THROW_ERROR_MSG(CAudioError::ERROR_OUT_OF_MEMORY, "Failed pa_proplist_new()");
169         }
170
171         // Adds values on proplist for delivery to PULSEAUDIO
172         char *streamType = NULL;
173         CAudioInfo::EAudioType audioType = __mSpec.getAudioInfo().getAudioType();
174         __mSpec.getAudioInfo().convertAudioType2StreamType(audioType, &streamType);
175         pa_proplist_sets(__mpPropList, PA_PROP_MEDIA_ROLE, streamType);
176
177         int index = __mSpec.getAudioInfo().getAudioIndex();
178         if (index >= 0) {
179             pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_PARENT_ID, "%u", (unsigned int) index);
180         }
181
182         // Adds latency on proplist for delivery to PULSEAUDIO
183         AUDIO_IO_LOGD("LATENCY : %s(%d)", __mSpec.getStreamLatencyToString(), __mSpec.getStreamLatency());
184         pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_TIZEN_AUDIO_LATENCY, "%s", __mSpec.getStreamLatencyToString());
185
186         // Allocates PA mainloop
187         __mpMainloop = pa_threaded_mainloop_new();
188         if (__mpMainloop == NULL) {
189             THROW_ERROR_MSG(CAudioError::ERROR_OUT_OF_MEMORY, "Failed pa_threaded_mainloop_new()");
190         }
191
192         // Allocates PA context
193         __mpContext = pa_context_new(pa_threaded_mainloop_get_api(__mpMainloop), CLIENT_NAME);
194         if (__mpContext == NULL) {
195             THROW_ERROR_MSG(CAudioError::ERROR_OUT_OF_MEMORY, "Failed pa_context_new()");
196         }
197
198         // Sets context state changed callback
199         pa_context_set_state_callback(__mpContext, __contextStateChangeCb, this);
200
201         // Connects this client with PA server
202         if (pa_context_connect(__mpContext, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0) {
203             THROW_ERROR_MSG(CAudioError::ERROR_OUT_OF_MEMORY, "Failed pa_context_connect()");
204         }
205
206         // LOCK for synchronous connection
207         pa_threaded_mainloop_lock(__mpMainloop);
208
209         // Start mainloop
210         if (pa_threaded_mainloop_start(__mpMainloop) < 0) {
211             pa_threaded_mainloop_unlock(__mpMainloop);
212             THROW_ERROR_MSG(CAudioError::ERROR_FAILED_OPERATION, "Failed pa_threaded_mainloop_start()");
213         }
214
215         // Connection process is asynchronously
216         // So, this function will be waited when occurred context state change event
217         // If I got a signal, do next processing
218         while (true) {
219             pa_context_state_t state;
220             state = pa_context_get_state(__mpContext);
221
222             if (state == PA_CONTEXT_READY) {
223                 break;
224             }
225
226             if (!PA_CONTEXT_IS_GOOD(state)) {
227                 err = pa_context_errno(__mpContext);
228                 pa_threaded_mainloop_unlock(__mpMainloop);
229                 THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_INTERNAL_OPERATION, "pa_context's state is not good err:[%d]", err);
230             }
231
232             /* Wait until the context is ready */
233             pa_threaded_mainloop_wait(__mpMainloop);
234         }
235
236         // Allocates PA stream
237         pa_sample_spec ss   = __mSpec.getSampleSpec();
238         pa_channel_map map  = __mSpec.getChannelMap();
239
240         __mpStream = pa_stream_new_with_proplist(__mpContext, __mSpec.getStreamName(), &ss, &map, __mpPropList);
241         if (__mpStream == NULL) {
242             pa_threaded_mainloop_unlock(__mpMainloop);
243             THROW_ERROR_MSG(CAudioError::ERROR_FAILED_OPERATION, "Failed pa_stream_new_with_proplist()()");
244         }
245
246         // Sets stream callbacks
247         pa_stream_set_state_callback(__mpStream, __streamStateChangeCb, this);
248         pa_stream_set_read_callback(__mpStream, __streamCaptureCb, this);
249         pa_stream_set_write_callback(__mpStream, __streamPlaybackCb, this);
250         pa_stream_set_latency_update_callback(__mpStream, __streamLatencyUpdateCb, this);
251
252         // Connect stream with PA Server
253
254         if (__mDirection == STREAM_DIRECTION_PLAYBACK) {
255             pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(
256                     PA_STREAM_INTERPOLATE_TIMING |
257                     PA_STREAM_ADJUST_LATENCY     |
258                     PA_STREAM_AUTO_TIMING_UPDATE);
259
260             ret = pa_stream_connect_playback(__mpStream, NULL, NULL, flags, NULL, NULL);
261         } else {
262             pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(
263                     PA_STREAM_INTERPOLATE_TIMING |
264                     PA_STREAM_ADJUST_LATENCY     |
265                     PA_STREAM_AUTO_TIMING_UPDATE);
266
267             ret = pa_stream_connect_record(__mpStream, NULL, NULL, flags);
268         }
269
270         if (ret != 0) {
271             err = pa_context_errno(__mpContext);
272             pa_threaded_mainloop_unlock(__mpMainloop);
273             THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_FAILED_OPERATION, "Failed pa_stream_connect() err:[%d]", err);
274         }
275
276         while (true) {
277             pa_stream_state_t state;
278             state = pa_stream_get_state(__mpStream);
279
280             if (state == PA_STREAM_READY) {
281                 AUDIO_IO_LOGD("STREAM READY");
282                 break;
283             }
284
285             if (!PA_STREAM_IS_GOOD(state)) {
286                 err = pa_context_errno(__mpContext);
287                 pa_threaded_mainloop_unlock(__mpMainloop);
288                 THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_INTERNAL_OPERATION, "pa_stream's state is not good err:[%d]", err);
289             }
290
291             /* Wait until the stream is ready */
292             pa_threaded_mainloop_wait(__mpMainloop);
293         }
294
295         // End of synchronous
296         pa_threaded_mainloop_unlock(__mpMainloop);
297
298         __mIsInit = true;
299     } catch (CAudioError e) {
300         finalize();
301         throw e;
302     }
303 }
304
305 void CPulseAudioClient::finalize() {
306     AUDIO_IO_LOGD("");
307     if (__mIsInit == false) {
308         return;
309     }
310
311     if (__mpMainloop != NULL) {
312         pa_threaded_mainloop_stop(__mpMainloop);
313     }
314     if (__mpStream != NULL) {
315         pa_stream_disconnect(__mpStream);
316         __mpStream = NULL;
317     }
318
319     if (__mpContext != NULL) {
320         pa_context_disconnect(__mpContext);
321         pa_context_unref(__mpContext);
322         __mpContext = NULL;
323     }
324
325     if (__mpMainloop != NULL) {
326         pa_threaded_mainloop_free(__mpMainloop);
327         __mpMainloop = NULL;
328     }
329
330     if (__mpPropList != NULL) {
331         pa_proplist_free(__mpPropList);
332         __mpPropList = NULL;
333     }
334
335     __mIsInit = false;
336 }
337
338 int CPulseAudioClient::peek(const void** data, size_t* length) throw (CAudioError) {
339     if (__mIsInit == false) {
340         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
341     }
342
343 #ifdef _AUDIO_IO_DEBUG_TIMING_
344     AUDIO_IO_LOGD("data:[%p], length:[%p]", data, length);
345 #endif
346
347     checkRunningState();
348
349     if (data == NULL || length == NULL) {
350         THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_INVALID_ARGUMENT, "The parameter is invalid - data:%p, length:%p", data, length);
351     }
352
353     if (__mDirection == STREAM_DIRECTION_PLAYBACK) {
354         THROW_ERROR_MSG(CAudioError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function");
355     }
356
357     int ret = 0;
358
359     if (isInThread() == false) {
360         pa_threaded_mainloop_lock(__mpMainloop);
361         ret = pa_stream_peek(__mpStream, data, length);
362         pa_threaded_mainloop_unlock(__mpMainloop);
363     } else {
364         ret = pa_stream_peek(__mpStream, data, length);
365     }
366
367     if (ret < 0) {
368         THROW_ERROR_MSG(CAudioError::ERROR_FAILED_OPERATION, "Failed pa_stream_peek()");
369     }
370
371     return ret;
372 }
373
374 int CPulseAudioClient::drop() throw (CAudioError) {
375     if (__mIsInit == false) {
376         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
377     }
378
379 #ifdef _AUDIO_IO_DEBUG_TIMING_
380     AUDIO_IO_LOGD("");
381 #endif
382
383     checkRunningState();
384
385     if (__mDirection == STREAM_DIRECTION_PLAYBACK) {
386         THROW_ERROR_MSG(CAudioError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function");
387     }
388
389     int ret = 0;
390
391     if (isInThread() == false) {
392         pa_threaded_mainloop_lock(__mpMainloop);
393         ret = pa_stream_drop(__mpStream);
394         pa_threaded_mainloop_unlock(__mpMainloop);
395     } else {
396         ret = pa_stream_drop(__mpStream);
397     }
398
399     if (ret < 0) {
400         THROW_ERROR_MSG(CAudioError::ERROR_FAILED_OPERATION, "Failed pa_stream_drop()");
401     }
402
403     return ret;
404 }
405
406 int CPulseAudioClient::write(const void* data, size_t length) throw (CAudioError) {
407     if (__mIsInit == false) {
408         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
409     }
410
411 #ifdef _AUDIO_IO_DEBUG_TIMING_
412     AUDIO_IO_LOGD("data[%p], length:[%d]", data, length);
413 #endif
414
415     checkRunningState();
416
417     if (data == NULL || length < 0) {
418         THROW_ERROR_MSG(CAudioError::ERROR_INVALID_ARGUMENT, "The parameter is invalid");
419     }
420
421     if (__mDirection == STREAM_DIRECTION_RECORD) {
422         THROW_ERROR_MSG(CAudioError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function");
423     }
424
425     int ret = 0;
426
427     if (isInThread() == false) {
428         pa_threaded_mainloop_lock(__mpMainloop);
429         ret = pa_stream_write(__mpStream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
430         pa_threaded_mainloop_unlock(__mpMainloop);
431     } else {
432         ret = pa_stream_write(__mpStream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
433     }
434
435     if (ret < 0) {
436         THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_FAILED_OPERATION, "Failed pa_stream_write() err:%d", ret);
437     }
438
439     return ret;
440 }
441
442 void CPulseAudioClient::cork(bool cork) throw (CAudioError) {
443     AUDIO_IO_LOGD("bool cork:%d", cork);
444
445     if (__mIsInit == false) {
446         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
447     }
448
449     if (isInThread() == true) {
450         THROW_ERROR_MSG(CAudioError::ERROR_NOT_SUPPORTED, "This operation is not supported in callback");
451     }
452
453     checkRunningState();
454
455     if (isInThread() == false) {
456         pa_threaded_mainloop_lock(__mpMainloop);
457         pa_operation_unref(pa_stream_cork(__mpStream, static_cast<int>(cork), __successStreamCb, this));
458         pa_threaded_mainloop_unlock(__mpMainloop);
459     } else {
460         pa_operation_unref(pa_stream_cork(__mpStream, static_cast<int>(cork), __successStreamCb, this));
461     }
462
463     return;
464 }
465
466 bool CPulseAudioClient::isCorked() throw (CAudioError) {
467     if (__mIsInit == false) {
468         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
469     }
470
471     checkRunningState();
472
473     int isCork = 0;
474
475     if (isInThread() == false) {
476         pa_threaded_mainloop_lock(__mpMainloop);
477         isCork = pa_stream_is_corked(__mpStream);
478         pa_threaded_mainloop_unlock(__mpMainloop);
479     } else {
480         isCork = pa_stream_is_corked(__mpStream);
481     }
482
483     AUDIO_IO_LOGD("isCork:%d", isCork);
484     return static_cast<bool>(isCork);
485 }
486
487 bool CPulseAudioClient::drain() throw (CAudioError) {
488     AUDIO_IO_LOGD("drain");
489
490     if (__mIsInit == false) {
491         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
492     }
493
494     checkRunningState();
495
496     if (isInThread() == false) {
497         pa_threaded_mainloop_lock(__mpMainloop);
498         pa_operation_unref(pa_stream_drain(__mpStream, __successStreamCb, this));
499         pa_threaded_mainloop_unlock(__mpMainloop);
500     } else {
501         pa_operation_unref(pa_stream_drain(__mpStream, __successStreamCb, this));
502     }
503
504     return true;
505 }
506
507 bool CPulseAudioClient::flush() throw (CAudioError) {
508     AUDIO_IO_LOGD("flush");
509
510     if (__mIsInit == false) {
511         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
512     }
513
514     checkRunningState();
515
516     if (isInThread() == false) {
517         pa_threaded_mainloop_lock(__mpMainloop);
518         pa_operation_unref(pa_stream_flush(__mpStream, __successStreamCb, this));
519         pa_threaded_mainloop_unlock(__mpMainloop);
520     } else {
521         pa_operation_unref(pa_stream_flush(__mpStream, __successStreamCb, this));
522     }
523
524     return true;
525 }
526
527 size_t CPulseAudioClient::getWritableSize() throw (CAudioError) {
528     if (__mIsInit == false) {
529         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
530     }
531
532     checkRunningState();
533
534     if (__mDirection != STREAM_DIRECTION_PLAYBACK) {
535         THROW_ERROR_MSG(CAudioError::ERROR_NOT_SUPPORTED, "This client is used for Playback");
536     }
537
538     size_t ret = 0;
539
540     if (isInThread() == false) {
541         pa_threaded_mainloop_lock(__mpMainloop);
542         ret = pa_stream_writable_size(__mpStream);
543         pa_threaded_mainloop_unlock(__mpMainloop);
544     } else {
545         ret = pa_stream_writable_size(__mpStream);
546     }
547
548     return ret;
549 }
550
551 void CPulseAudioClient::checkRunningState() throw (CAudioError) {
552     if (__mIsInit == false) {
553         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
554     }
555
556     if (__mpContext == NULL || PA_CONTEXT_IS_GOOD(pa_context_get_state(__mpContext)) == 0) {
557         THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_NOT_INITIALIZED, "The context[%p] is not created or not good state", __mpContext);
558     }
559     if (__mpStream == NULL || PA_STREAM_IS_GOOD(pa_stream_get_state(__mpStream)) == 0) {
560         THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_NOT_INITIALIZED, "The stream[%p] is not created or not good state", __mpStream);
561     }
562     if (pa_context_get_state(__mpContext) != PA_CONTEXT_READY || pa_stream_get_state(__mpStream)   != PA_STREAM_READY) {
563         THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_NOT_INITIALIZED, "The context[%p] or stream[%p] state is not ready", __mpContext, __mpStream);
564     }
565
566 #ifdef _AUDIO_IO_DEBUG_TIMING_
567     AUDIO_IO_LOGD("This client is running");
568 #endif
569 }
570
571 bool CPulseAudioClient::isInThread() throw (CAudioError) {
572     if (__mIsInit == false) {
573         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
574     }
575
576     int ret = pa_threaded_mainloop_in_thread(__mpMainloop);
577
578 #ifdef _AUDIO_IO_DEBUG_TIMING_
579     AUDIO_IO_LOGD("isInThread : [%d][TRUE:1][FALSE:0]", ret);
580 #endif
581     return static_cast<bool>(ret);
582 }
583
584 size_t CPulseAudioClient::getReadableSize() throw (CAudioError) {
585     if (__mIsInit == false) {
586         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
587     }
588
589     checkRunningState();
590
591     if (__mDirection != STREAM_DIRECTION_RECORD) {
592         THROW_ERROR_MSG(CAudioError::ERROR_NOT_SUPPORTED, "This client is used for Capture");
593     }
594
595     size_t ret = 0;
596
597     if (isInThread() == false) {
598         pa_threaded_mainloop_lock(__mpMainloop);
599         ret = pa_stream_writable_size(__mpStream);
600         pa_threaded_mainloop_unlock(__mpMainloop);
601     } else {
602         ret = pa_stream_writable_size(__mpStream);
603     }
604
605     return ret;
606 }
607
608 size_t CPulseAudioClient::getBufferSize() throw (CAudioError) {
609     if (__mIsInit == false) {
610         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
611     }
612
613     checkRunningState();
614
615     size_t ret = 0;
616
617     try {
618         if (isInThread() == false) {
619             pa_threaded_mainloop_lock(__mpMainloop);
620         }
621
622         const pa_buffer_attr* attr = pa_stream_get_buffer_attr(__mpStream);
623         if (attr == NULL) {
624             int _err = pa_context_errno(__mpContext);
625             THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_buffer_attr() err:%d", _err);
626         }
627
628         if (__mDirection == STREAM_DIRECTION_PLAYBACK) {
629             ret = attr->tlength;
630             AUDIO_IO_LOGD("PLAYBACK buffer size : %d", ret);
631         } else {
632             ret = attr->fragsize;
633             AUDIO_IO_LOGD("RECORD buffer size : %d", ret);
634         }
635     } catch (CAudioError err) {
636         if (isInThread() == false) {
637             pa_threaded_mainloop_unlock(__mpMainloop);
638         }
639         throw err;
640     }
641
642     if (isInThread() == false) {
643         pa_threaded_mainloop_unlock(__mpMainloop);
644     }
645
646     return ret;
647 }
648
649 pa_usec_t CPulseAudioClient::getLatency() throw (CAudioError) {
650     if (__mIsInit == false) {
651         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
652     }
653
654     checkRunningState();
655
656     pa_usec_t ret = 0;
657     int negative  = 0;
658
659     if (isInThread() == false) {
660         if (pa_stream_get_latency(__mpStream, &ret, &negative) < 0) {
661             int _err = pa_context_errno(__mpContext);
662             if (_err != PA_ERR_NODATA) {
663                 THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_latency() err:%d", _err);
664             }
665         }
666         return negative ? 0 : ret;
667     }
668
669         pa_threaded_mainloop_lock(__mpMainloop);
670
671     try {
672         while (true) {
673             if (pa_stream_get_latency(__mpStream, &ret, &negative) >= 0) {
674                 break;
675             }
676
677             int _err = pa_context_errno(__mpContext);
678             if (_err != PA_ERR_NODATA) {
679                 THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_latency() err:%d", _err);
680             }
681
682             /* Wait until latency data is available again */
683             pa_threaded_mainloop_wait(__mpMainloop);
684         }
685     } catch (CAudioError e) {
686         pa_threaded_mainloop_unlock(__mpMainloop);
687         throw e;
688     }
689
690     pa_threaded_mainloop_unlock(__mpMainloop);
691
692     return negative ? 0 : ret;
693 }
694
695 pa_usec_t CPulseAudioClient::getFinalLatency() throw (CAudioError) {
696     if (__mIsInit == false) {
697         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
698     }
699
700     checkRunningState();
701
702     pa_usec_t ret = 0;
703     uint32_t  ver = 0;
704
705     try {
706         if (isInThread() == false) {
707             pa_threaded_mainloop_lock(__mpMainloop);
708         }
709
710         ver = pa_context_get_server_protocol_version(__mpContext);
711         if (ver >= 13) {
712             const pa_buffer_attr* buffer_attr = pa_stream_get_buffer_attr(__mpStream);
713             const pa_sample_spec* sample_spec = pa_stream_get_sample_spec(__mpStream);
714             const pa_timing_info* timing_info = pa_stream_get_timing_info(__mpStream);
715
716             if (buffer_attr == NULL || sample_spec == NULL || timing_info == NULL) {
717                 THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_OUT_OF_MEMORY, "Failed to get buffer_attr[%p] or sample_spec[%p] or timing_info[%p] from a pa_stream",
718                         buffer_attr, sample_spec, timing_info);
719             }
720
721             if (__mDirection == STREAM_DIRECTION_PLAYBACK) {
722                 ret = (pa_bytes_to_usec(buffer_attr->tlength, sample_spec) + timing_info->configured_sink_usec);
723                 AUDIO_IO_LOGD("FINAL PLAYBACK LATENCY : %d", ret);
724             } else {
725                 ret = (pa_bytes_to_usec(buffer_attr->fragsize, sample_spec) + timing_info->configured_source_usec);
726                 AUDIO_IO_LOGD("FINAL RECORD LATENCY : %d", ret);
727             }
728         } else {
729             THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_NOT_SUPPORTED, "This version(ver.%d) is not supported", ver);
730         }
731
732         if (isInThread() == false) {
733             pa_threaded_mainloop_unlock(__mpMainloop);
734         }
735     } catch (CAudioError e) {
736         if (isInThread() == false) {
737             pa_threaded_mainloop_unlock(__mpMainloop);
738         }
739         throw e;
740     }
741
742     return ret;
743 }
744
745 CPulseAudioClient::EStreamDirection CPulseAudioClient::getStreamDirection() {
746     return __mDirection;
747 }
748
749 CPulseStreamSpec CPulseAudioClient::getStreamSpec() {
750     return __mSpec;
751 }