2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "CAudioIODef.h"
23 #include <dpm/restriction.h>
27 using namespace tizen_media_audio;
31 * class CPulseAudioClient
33 const char* CPulseAudioClient::CLIENT_NAME = "AUDIO_IO_PA_CLIENT";
34 static const unsigned int drain_wait_interval = 10000;
35 static const unsigned int drain_wait_max_count = 30;
37 CPulseAudioClient::CPulseAudioClient(
38 EStreamDirection direction,
39 CPulseStreamSpec& spec,
40 IPulseStreamListener* listener) :
41 __mDirection(direction),
43 __mpListener(listener),
49 __mIsOperationSuccess(false),
50 __mpSyncReadDataPtr(NULL),
53 __mIsUsedSyncRead(false),
54 __mIsFirstStream(false),
55 __mIsDraining(false) {
58 CPulseAudioClient::~CPulseAudioClient() {
62 void CPulseAudioClient::__contextStateChangeCb(pa_context* c, void* user_data) {
63 CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
67 switch (pa_context_get_state(c)) {
68 case PA_CONTEXT_READY:
69 AUDIO_IO_LOGD("The context is ready");
70 pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
73 case PA_CONTEXT_FAILED:
74 case PA_CONTEXT_TERMINATED:
75 AUDIO_IO_LOGD("The context is lost");
76 pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
79 case PA_CONTEXT_UNCONNECTED:
80 case PA_CONTEXT_CONNECTING:
81 case PA_CONTEXT_AUTHORIZING:
82 case PA_CONTEXT_SETTING_NAME:
87 void CPulseAudioClient::__successContextCb(pa_context* c, int success, void* user_data) {
88 AUDIO_IO_LOGD("pa_context[%p], success[%d], user_data[%p]", c, success, user_data);
92 CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
93 pClient->__mIsOperationSuccess = static_cast<bool>(success);
95 pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
98 static bool __is_microphone_restricted(void) {
101 device_policy_manager_h dpm_h = NULL;
104 if ((dpm_h = dpm_manager_create())) {
105 /* state: 0(disallowed), 1(allowed) */
106 if ((ret = dpm_restriction_get_microphone_state(dpm_h, &state)))
107 AUDIO_IO_LOGE("Failed to dpm_restriction_get_microphone_state(), ret(0x%x)", ret);
108 dpm_manager_destroy(dpm_h);
109 AUDIO_IO_LOGD("microphone restriction state: %d(1:allowed, 0:disallowed)", state);
111 AUDIO_IO_LOGE("Failed to dpm_manager_create()");
114 return (state ? false : true);
117 void CPulseAudioClient::__streamStateChangeCb(pa_stream* s, void* user_data) {
121 CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
123 switch (pa_stream_get_state(s)) {
124 case PA_STREAM_READY:
125 AUDIO_IO_LOGD("The stream is ready");
126 pClient->__mpListener->onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
127 if (pClient->__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK)
128 pClient->__mIsFirstStream = true;
129 pClient->__mIsInit = true;
130 pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
133 case PA_STREAM_FAILED:
134 AUDIO_IO_LOGD("The stream is failed");
135 pClient->__mpListener->onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE,
136 __is_microphone_restricted());
137 pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
140 case PA_STREAM_TERMINATED:
141 AUDIO_IO_LOGD("The stream is terminated");
142 pClient->__mpListener->onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
143 pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
146 case PA_STREAM_UNCONNECTED:
147 case PA_STREAM_CREATING:
152 void CPulseAudioClient::__streamCaptureCb(pa_stream* s, size_t length, void* user_data) {
156 CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
157 assert(pClient->__mpListener);
158 assert(pClient->__mpMainloop);
160 if (pClient->__mIsUsedSyncRead == true) {
161 pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
164 pClient->__mpListener->onStream(pClient, length);
167 void CPulseAudioClient::__streamPlaybackCb(pa_stream* s, size_t length, void* user_data) {
171 CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
172 assert(pClient->__mpListener);
174 pClient->__mpListener->onStream(pClient, length);
176 /* If stream is not written in first callback during prepare,
177 then write dummy data to ensure the start */
178 if (pClient->__mIsFirstStream) {
179 AUDIO_IO_LOGD("Write dummy, length [%d]", length);
181 char* dummy = new char[length];
182 memset(dummy, 0, length);
183 pa_stream_write(s, dummy, length, NULL, 0LL, PA_SEEK_RELATIVE);
186 pClient->__mIsFirstStream = false;
190 void CPulseAudioClient::__streamLatencyUpdateCb(pa_stream* s, void* user_data) {
194 CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
196 pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
199 void CPulseAudioClient::__streamEventCb(pa_stream* s, const char *name, pa_proplist *pl, void *user_data) {
203 AUDIO_IO_LOGE("NAME : %s, Prop : %p", name, pl);
205 CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
206 if (strcmp(name, PA_STREAM_EVENT_POP_TIMEOUT) == 0) {
207 pa_operation_unref(pa_stream_cork(pClient->__mpStream, 1, NULL, NULL));
212 void CPulseAudioClient::__successStreamCb(pa_stream* s, int success, void* user_data) {
213 AUDIO_IO_LOGD("pa_stream[%p], success[%d], user_data[%p]", s, success, user_data);
217 CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
218 pClient->__mIsOperationSuccess = static_cast<bool>(success);
220 pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
223 void CPulseAudioClient::__successDrainCbInThread(pa_stream* s, int success, void* user_data) {
224 AUDIO_IO_LOGD("pa_stream[%p], success[%d], user_data[%p]", s, success, user_data);
228 CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
229 pClient->__mIsOperationSuccess = static_cast<bool>(success);
230 pClient->__mIsDraining = false;
233 void CPulseAudioClient::__successDrainCb(pa_stream* s, int success, void* user_data) {
234 AUDIO_IO_LOGD("pa_stream[%p], success[%d], user_data[%p]", s, success, user_data);
238 CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
239 pClient->__mIsOperationSuccess = static_cast<bool>(success);
240 pClient->__mIsDraining = false;
242 pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
245 void CPulseAudioClient::initialize() {
246 if (__mIsInit == true) {
254 // Allocates PA proplist
255 __mpPropList = pa_proplist_new();
256 if (__mpPropList == NULL) {
257 THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_proplist_new()");
260 // Adds values on proplist for delivery to PULSEAUDIO
261 char *streamType = NULL;
262 CAudioInfo::EAudioType audioType = __mSpec.getAudioInfo().getAudioType();
263 __mSpec.getAudioInfo().convertAudioType2StreamType(audioType, &streamType);
264 pa_proplist_sets(__mpPropList, PA_PROP_MEDIA_ROLE, streamType);
266 int index = __mSpec.getAudioInfo().getAudioIndex();
268 pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_PARENT_ID, "%u", (unsigned int) index);
271 // Adds latency on proplist for delivery to PULSEAUDIO
272 AUDIO_IO_LOGD("LATENCY : %s[%d]", __mSpec.getStreamLatencyToString(), static_cast<int>(__mSpec.getStreamLatency()));
273 pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_TIZEN_AUDIO_LATENCY, "%s", __mSpec.getStreamLatencyToString());
275 // Allocates PA mainloop
276 __mpMainloop = pa_threaded_mainloop_new();
277 if (__mpMainloop == NULL) {
278 THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_threaded_mainloop_new()");
281 // Allocates PA context
282 __mpContext = pa_context_new(pa_threaded_mainloop_get_api(__mpMainloop), CLIENT_NAME);
283 if (__mpContext == NULL) {
284 THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_context_new()");
287 // Sets context state changed callback
288 pa_context_set_state_callback(__mpContext, __contextStateChangeCb, this);
290 // Connects this client with PA server
291 if (pa_context_connect(__mpContext, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0) {
292 THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_context_connect()");
295 // LOCK for synchronous connection
296 pa_threaded_mainloop_lock(__mpMainloop);
299 if (pa_threaded_mainloop_start(__mpMainloop) < 0) {
300 pa_threaded_mainloop_unlock(__mpMainloop);
301 THROW_ERROR_MSG(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_threaded_mainloop_start()");
304 // Connection process is asynchronously
305 // So, this function will be waited when occurred context state change event
306 // If I got a signal, do next processing
308 pa_context_state_t state;
309 state = pa_context_get_state(__mpContext);
311 if (state == PA_CONTEXT_READY) {
315 if (!PA_CONTEXT_IS_GOOD(state)) {
316 err = pa_context_errno(__mpContext);
317 pa_threaded_mainloop_unlock(__mpMainloop);
318 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "pa_context's state is not good : err[%d]", err);
321 /* Wait until the context is ready */
322 pa_threaded_mainloop_wait(__mpMainloop);
325 // Allocates PA stream
326 pa_sample_spec ss = __mSpec.getSampleSpec();
327 pa_channel_map map = __mSpec.getChannelMap();
329 __mpStream = pa_stream_new_with_proplist(__mpContext, __mSpec.getStreamName(), &ss, &map, __mpPropList);
330 if (__mpStream == NULL) {
331 pa_threaded_mainloop_unlock(__mpMainloop);
332 THROW_ERROR_MSG(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_new_with_proplist()");
335 // Sets stream callbacks
336 pa_stream_set_state_callback(__mpStream, __streamStateChangeCb, this);
337 pa_stream_set_read_callback(__mpStream, __streamCaptureCb, this);
338 pa_stream_set_write_callback(__mpStream, __streamPlaybackCb, this);
339 pa_stream_set_latency_update_callback(__mpStream, __streamLatencyUpdateCb, this);
340 pa_stream_set_event_callback(__mpStream, __streamEventCb, this);
342 // Connect stream with PA Server
344 if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
345 pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(
346 PA_STREAM_INTERPOLATE_TIMING |
347 PA_STREAM_ADJUST_LATENCY |
348 PA_STREAM_AUTO_TIMING_UPDATE);
350 ret = pa_stream_connect_playback(__mpStream, NULL, NULL, flags, NULL, NULL);
352 pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(
353 PA_STREAM_INTERPOLATE_TIMING |
354 PA_STREAM_ADJUST_LATENCY |
355 PA_STREAM_AUTO_TIMING_UPDATE);
357 ret = pa_stream_connect_record(__mpStream, NULL, NULL, flags);
361 err = pa_context_errno(__mpContext);
362 pa_threaded_mainloop_unlock(__mpMainloop);
363 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_connect() : err[%d]", err);
367 pa_stream_state_t state;
368 state = pa_stream_get_state(__mpStream);
370 if (state == PA_STREAM_READY) {
371 AUDIO_IO_LOGD("STREAM READY");
375 if (!PA_STREAM_IS_GOOD(state)) {
376 err = pa_context_errno(__mpContext);
377 pa_threaded_mainloop_unlock(__mpMainloop);
378 if (err == PA_ERR_ACCESS) {
379 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_DEVICE_POLICY_RESTRICTION, "pa_stream's state is not good : err[%d]", err);
381 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "pa_stream's state is not good : err[%d]", err);
385 /* Wait until the stream is ready */
386 pa_threaded_mainloop_wait(__mpMainloop);
389 // End of synchronous
390 pa_threaded_mainloop_unlock(__mpMainloop);
392 // __mIsInit = true; // Moved to __streamStateChangeCb()
393 } catch (CAudioError& e) {
399 void CPulseAudioClient::finalize() {
401 if (__mIsInit == false) {
405 if (isInThread() == false)
406 pa_threaded_mainloop_lock(__mpMainloop);
408 /* clear callbacks */
409 if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK)
410 pa_stream_set_write_callback(__mpStream, NULL, NULL);
412 pa_stream_set_read_callback(__mpStream, NULL, NULL);
413 pa_stream_set_latency_update_callback(__mpStream, NULL, NULL);
414 pa_stream_set_event_callback(__mpStream, NULL, NULL);
416 if (isInThread() == false)
417 pa_threaded_mainloop_unlock(__mpMainloop);
419 /* Wait for drain complete if draining before finalize */
421 unsigned int drain_wait_count = 0;
422 while (__mIsDraining && drain_wait_count++ < drain_wait_max_count) {
423 usleep(drain_wait_interval);
425 AUDIO_IO_LOGD("wait for drain complete!!!! [%d * %d usec]",
426 drain_wait_count, drain_wait_interval);
429 if (__mpMainloop != NULL) {
430 pa_threaded_mainloop_stop(__mpMainloop);
433 if (__mpStream != NULL) {
434 pa_stream_disconnect(__mpStream);
435 pa_stream_unref(__mpStream);
439 if (__mpContext != NULL) {
440 pa_context_disconnect(__mpContext);
441 pa_context_unref(__mpContext);
445 if (__mpMainloop != NULL) {
446 pa_threaded_mainloop_free(__mpMainloop);
450 if (__mpPropList != NULL) {
451 pa_proplist_free(__mpPropList);
458 int CPulseAudioClient::read(void* buffer, size_t length) {
459 if (__mIsInit == false) {
460 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
465 if (buffer == NULL) {
466 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "The parameter is invalid : buffer[%p]", buffer);
469 if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
470 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function");
473 size_t lengthIter = length;
476 __mIsUsedSyncRead = true;
479 pa_threaded_mainloop_lock(__mpMainloop);
481 while (lengthIter > 0) {
484 while (__mpSyncReadDataPtr == NULL) {
485 ret = pa_stream_peek(__mpStream, &__mpSyncReadDataPtr, &__mSyncReadLength);
487 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pa_stream_peek() : ret[%d]", ret);
490 if (__mSyncReadLength <= 0) {
491 #ifdef _AUDIO_IO_DEBUG_TIMING_
492 AUDIO_IO_LOGD("readLength(%d byte) is not valid. wait...", __mSyncReadLength);
494 pa_threaded_mainloop_wait(__mpMainloop);
495 } else if (__mpSyncReadDataPtr == NULL) {
496 // Data peeked, but it doesn't have any data
497 ret = pa_stream_drop(__mpStream);
499 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pa_stream_drop() : ret[%d]", ret);
502 __mSyncReadIndex = 0;
506 if (__mSyncReadLength < lengthIter) {
507 l = __mSyncReadLength;
512 // Copy partial pcm data on out parameter
513 #ifdef _AUDIO_IO_DEBUG_TIMING_
514 AUDIO_IO_LOGD("memcpy() that a peeked buffer[0x%x], index[%d], length[%d] on out buffer", (const uint8_t*)(__mpSyncReadDataPtr) + __mSyncReadIndex, __mSyncReadIndex, l);
516 memcpy(buffer, (const uint8_t*)__mpSyncReadDataPtr + __mSyncReadIndex, l);
518 // Move next position
519 buffer = (uint8_t*)buffer + l;
522 // Adjusts the rest length
523 __mSyncReadIndex += l;
524 __mSyncReadLength -= l;
526 if (__mSyncReadLength == 0) {
527 #ifdef _AUDIO_IO_DEBUG_TIMING_
528 AUDIO_IO_LOGD("__mSyncReadLength is zero, do drop()");
530 ret = pa_stream_drop(__mpStream);
532 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pa_stream_drop() : ret[%d]", ret);
535 // Reset the internal pointer
536 __mpSyncReadDataPtr = NULL;
537 __mSyncReadLength = 0;
538 __mSyncReadIndex = 0;
540 } // End of while (lengthIter > 0)
542 pa_threaded_mainloop_unlock(__mpMainloop);
543 __mIsUsedSyncRead = false;
544 } catch (CAudioError& e) {
545 pa_threaded_mainloop_unlock(__mpMainloop);
546 __mIsUsedSyncRead = false;
553 int CPulseAudioClient::peek(const void** buffer, size_t* length) {
554 if (__mIsInit == false) {
555 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
560 if (buffer == NULL || length == NULL) {
561 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "The parameter is invalid : buffer[%p], length[%p]", buffer, length);
564 if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
565 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function");
570 if (isInThread() == false) {
571 pa_threaded_mainloop_lock(__mpMainloop);
572 ret = pa_stream_peek(__mpStream, buffer, length);
573 pa_threaded_mainloop_unlock(__mpMainloop);
575 ret = pa_stream_peek(__mpStream, buffer, length);
578 #ifdef _AUDIO_IO_DEBUG_TIMING_
579 AUDIO_IO_LOGD("buffer[%p], length[%d]", *buffer, *length);
583 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_peek() : err[%d]", ret);
589 int CPulseAudioClient::drop() {
590 if (__mIsInit == false) {
591 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
594 #ifdef _AUDIO_IO_DEBUG_TIMING_
600 if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
601 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function");
606 if (isInThread() == false) {
607 pa_threaded_mainloop_lock(__mpMainloop);
608 ret = pa_stream_drop(__mpStream);
609 pa_threaded_mainloop_unlock(__mpMainloop);
611 ret = pa_stream_drop(__mpStream);
615 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_drop() : err[%d]", ret);
621 int CPulseAudioClient::write(const void* data, size_t length) {
623 THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_ARGUMENT, "The parameter is invalid");
626 if (__mDirection == EStreamDirection::STREAM_DIRECTION_RECORD) {
627 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function");
632 #ifdef _AUDIO_IO_DEBUG_TIMING_
633 AUDIO_IO_LOGD("data[%p], length[%d]", data, length);
635 if (pa_stream_is_corked(__mpStream)) {
636 AUDIO_IO_LOGW("stream is corked...do uncork here first!!!!");
637 pa_operation_unref(pa_stream_cork(__mpStream, 0, NULL, this));
640 if (isInThread() == false) {
641 pa_threaded_mainloop_lock(__mpMainloop);
642 ret = pa_stream_write(__mpStream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
643 pa_threaded_mainloop_unlock(__mpMainloop);
645 if (__mIsFirstStream) {
646 const pa_buffer_attr* attr = pa_stream_get_buffer_attr(__mpStream);
647 uint32_t prebuf = attr->prebuf;
648 // Compensate amount of prebuf in first stream callback when audio-io use prebuf(-1)
649 // Because a stream will never start when an application wrote less than prebuf at first
650 if (length < prebuf) {
651 char* dummy = new char[prebuf - length];
652 memset(dummy, 0, prebuf - length);
653 pa_stream_write(__mpStream, dummy, prebuf - length, NULL, 0LL, PA_SEEK_RELATIVE);
656 __mIsFirstStream = false;
657 AUDIO_IO_LOGW("FIRST STREAM CALLBACK : length[%d], prebuf[%d], dummy[%d]",
658 length, prebuf, (length < prebuf) ? prebuf - length : 0);
660 ret = pa_stream_write(__mpStream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
664 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_write() : err[%d]", ret);
670 void CPulseAudioClient::cork(bool cork) {
671 AUDIO_IO_LOGD("cork[%d]", cork);
673 if (__mIsInit == false) {
674 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
677 if (isInThread() == true) {
678 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This operation is not supported in callback");
683 // Set __mIsFirstStream flag when uncork(resume) stream, because prebuf will be enable again
685 __mIsFirstStream = true;
687 if (isInThread() == false) {
688 pa_threaded_mainloop_lock(__mpMainloop);
689 pa_operation_unref(pa_stream_cork(__mpStream, static_cast<int>(cork), __successStreamCb, this));
690 pa_threaded_mainloop_unlock(__mpMainloop);
692 pa_operation_unref(pa_stream_cork(__mpStream, static_cast<int>(cork), __successStreamCb, this));
698 bool CPulseAudioClient::isCorked() {
699 if (__mIsInit == false) {
700 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
707 if (isInThread() == false) {
708 pa_threaded_mainloop_lock(__mpMainloop);
709 isCork = pa_stream_is_corked(__mpStream);
710 pa_threaded_mainloop_unlock(__mpMainloop);
712 isCork = pa_stream_is_corked(__mpStream);
715 AUDIO_IO_LOGD("isCork[%d]", isCork);
716 return static_cast<bool>(isCork);
719 bool CPulseAudioClient::drain() {
720 if (__mIsInit == false) {
721 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
727 AUDIO_IO_LOGW("already draining...");
729 if (isInThread() == false) {
730 AUDIO_IO_LOGD("drain");
731 pa_threaded_mainloop_lock(__mpMainloop);
732 pa_operation* o = pa_stream_drain(__mpStream, __successDrainCb, this);
733 __mIsDraining = true;
734 while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
735 pa_threaded_mainloop_wait(__mpMainloop);
737 pa_operation_unref(o);
738 pa_threaded_mainloop_unlock(__mpMainloop);
740 AUDIO_IO_LOGD("drain in thread");
741 pa_operation_unref(pa_stream_drain(__mpStream, __successDrainCbInThread, this));
742 __mIsDraining = true;
748 bool CPulseAudioClient::flush() {
749 if (__mIsInit == false) {
750 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
755 if (isInThread() == false) {
756 AUDIO_IO_LOGD("flush");
757 pa_threaded_mainloop_lock(__mpMainloop);
758 pa_operation_unref(pa_stream_flush(__mpStream, __successStreamCb, this));
759 pa_threaded_mainloop_unlock(__mpMainloop);
761 AUDIO_IO_LOGD("flush in thread");
762 pa_operation_unref(pa_stream_flush(__mpStream, __successStreamCb, this));
768 size_t CPulseAudioClient::getWritableSize() {
769 if (__mIsInit == false) {
770 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
775 if (__mDirection != EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
776 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This client is used for Playback");
781 if (isInThread() == false) {
782 pa_threaded_mainloop_lock(__mpMainloop);
783 ret = pa_stream_writable_size(__mpStream);
784 pa_threaded_mainloop_unlock(__mpMainloop);
786 ret = pa_stream_writable_size(__mpStream);
792 void CPulseAudioClient::checkRunningState() {
793 if (__mpContext == NULL || PA_CONTEXT_IS_GOOD(pa_context_get_state(__mpContext)) == 0) {
794 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_INITIALIZED, "The context[%p] is not created or not good state", __mpContext);
796 if (__mpStream == NULL || PA_STREAM_IS_GOOD(pa_stream_get_state(__mpStream)) == 0) {
797 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_INITIALIZED, "The stream[%p] is not created or not good state", __mpStream);
799 if (pa_context_get_state(__mpContext) != PA_CONTEXT_READY || pa_stream_get_state(__mpStream) != PA_STREAM_READY) {
800 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_INITIALIZED, "The context[%p] or stream[%p] state is not ready", __mpContext, __mpStream);
803 #ifdef _AUDIO_IO_DEBUG_TIMING_
804 AUDIO_IO_LOGD("This client is running");
808 bool CPulseAudioClient::isInThread() {
809 int ret = pa_threaded_mainloop_in_thread(__mpMainloop);
811 #ifdef _AUDIO_IO_DEBUG_TIMING_
812 AUDIO_IO_LOGD("isInThread[%d]", ret);
814 return static_cast<bool>(ret);
817 size_t CPulseAudioClient::getReadableSize() {
818 if (__mIsInit == false) {
819 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
824 if (__mDirection != EStreamDirection::STREAM_DIRECTION_RECORD) {
825 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This client is used for Capture");
830 if (isInThread() == false) {
831 pa_threaded_mainloop_lock(__mpMainloop);
832 ret = pa_stream_writable_size(__mpStream);
833 pa_threaded_mainloop_unlock(__mpMainloop);
835 ret = pa_stream_writable_size(__mpStream);
841 size_t CPulseAudioClient::getBufferSize() {
842 if (__mIsInit == false) {
843 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
851 if (isInThread() == false) {
852 pa_threaded_mainloop_lock(__mpMainloop);
855 const pa_buffer_attr* attr = pa_stream_get_buffer_attr(__mpStream);
857 int _err = pa_context_errno(__mpContext);
858 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_buffer_attr() : err[%d]", _err);
861 if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
863 AUDIO_IO_LOGD("PLAYBACK buffer size[%d]", ret);
865 ret = attr->fragsize;
866 AUDIO_IO_LOGD("RECORD buffer size[%d]", ret);
868 } catch (CAudioError& e) {
869 if (isInThread() == false) {
870 pa_threaded_mainloop_unlock(__mpMainloop);
875 if (isInThread() == false) {
876 pa_threaded_mainloop_unlock(__mpMainloop);
882 pa_usec_t CPulseAudioClient::getLatency() {
883 if (__mIsInit == false) {
884 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
892 if (isInThread() == false) {
893 if (pa_stream_get_latency(__mpStream, &ret, &negative) < 0) {
894 int _err = pa_context_errno(__mpContext);
895 if (_err != PA_ERR_NODATA) {
896 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_latency() : err[%d]", _err);
899 return negative ? 0 : ret;
902 pa_threaded_mainloop_lock(__mpMainloop);
906 if (pa_stream_get_latency(__mpStream, &ret, &negative) >= 0) {
910 int _err = pa_context_errno(__mpContext);
911 if (_err != PA_ERR_NODATA) {
912 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_latency() : err[%d]", _err);
915 /* Wait until latency data is available again */
916 pa_threaded_mainloop_wait(__mpMainloop);
918 } catch (CAudioError& e) {
919 pa_threaded_mainloop_unlock(__mpMainloop);
923 pa_threaded_mainloop_unlock(__mpMainloop);
925 return negative ? 0 : ret;
928 pa_usec_t CPulseAudioClient::getFinalLatency() {
929 if (__mIsInit == false) {
930 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
939 if (isInThread() == false) {
940 pa_threaded_mainloop_lock(__mpMainloop);
943 ver = pa_context_get_server_protocol_version(__mpContext);
945 const pa_buffer_attr* buffer_attr = pa_stream_get_buffer_attr(__mpStream);
946 const pa_sample_spec* sample_spec = pa_stream_get_sample_spec(__mpStream);
947 const pa_timing_info* timing_info = pa_stream_get_timing_info(__mpStream);
949 if (buffer_attr == NULL || sample_spec == NULL || timing_info == NULL) {
950 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed to get buffer_attr[%p] or sample_spec[%p] or timing_info[%p] from a pa_stream",
951 buffer_attr, sample_spec, timing_info);
954 if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
955 ret = (pa_bytes_to_usec(buffer_attr->tlength, sample_spec) + timing_info->configured_sink_usec);
956 AUDIO_IO_LOGD("FINAL PLAYBACK LATENCY[%" PRIu64 "]", ret);
958 ret = (pa_bytes_to_usec(buffer_attr->fragsize, sample_spec) + timing_info->configured_source_usec);
959 AUDIO_IO_LOGD("FINAL RECORD LATENCY[%" PRIu64 "]", ret);
962 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_SUPPORTED, "This version(ver.%d) is not supported", ver);
965 if (isInThread() == false) {
966 pa_threaded_mainloop_unlock(__mpMainloop);
968 } catch (CAudioError& e) {
969 if (isInThread() == false) {
970 pa_threaded_mainloop_unlock(__mpMainloop);
978 CPulseAudioClient::EStreamDirection CPulseAudioClient::getStreamDirection() {
982 CPulseStreamSpec CPulseAudioClient::getStreamSpec() {