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