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.
18 #include "CAudioIODef.h"
22 #include <dpm/restriction.h>
26 using namespace tizen_media_audio;
30 * class CPulseAudioClient
32 const char* CPulseAudioClient::CLIENT_NAME = "AUDIO_IO_PA_CLIENT";
33 static const unsigned int drain_wait_interval = 10000;
34 static const unsigned int drain_wait_max_count = 30;
36 CPulseAudioClient::CPulseAudioClient(
37 EStreamDirection direction,
38 CPulseStreamSpec& spec,
39 IPulseStreamListener* listener) :
40 __mDirection(direction),
42 __mpListener(listener),
48 __mIsOperationSuccess(false),
49 __mpSyncReadDataPtr(NULL),
52 __mIsUsedSyncRead(false),
53 __mIsFirstStream(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 #ifndef DISABLE_MOBILE_BACK_COMP
168 static void __dummy_write(pa_stream* s, size_t length) {
169 char* dummy = new char[length];
170 memset(dummy, 0, length);
171 pa_stream_write(s, dummy, length, NULL, 0LL, PA_SEEK_RELATIVE);
176 void CPulseAudioClient::__streamPlaybackCb(pa_stream* s, size_t length, void* user_data) {
180 CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
181 assert(pClient->__mpListener);
183 #ifndef DISABLE_MOBILE_BACK_COMP
184 if (pClient->__mIsInit == false) {
185 AUDIO_IO_LOGD("Occurred this listener when an out stream is on the way to create : Write dummy, length[%zu]", length);
186 __dummy_write(s, length);
189 if (pClient->isCorked()) {
190 AUDIO_IO_LOGD("Occurred this listener when an out stream is CORKED : Write dummy, length[%zu]", length);
191 __dummy_write(s, length);
196 pClient->__mpListener->onStream(pClient, length);
198 #ifndef DISABLE_MOBILE_BACK_COMP
199 /* If stream is not written in first callback during prepare,
200 then write dummy data to ensure the start */
201 if (pClient->__mSpec.getStreamLatency() == CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_OUTPUT_DEFAULT_ASYNC &&
202 pClient->__mIsFirstStream) {
203 AUDIO_IO_LOGW("[async] Write dummy of length[%zu] since not written in first callback during prepare", length);
204 __dummy_write(s, length);
205 pClient->__mIsFirstStream = false;
210 void CPulseAudioClient::__streamLatencyUpdateCb(pa_stream* s, void* user_data) {
214 CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
216 pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
219 void CPulseAudioClient::__streamStartedCb(pa_stream* s, void* user_data) {
223 CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
225 AUDIO_IO_LOGD("stream %p started.", pClient);
227 pClient->__mIsStarted = true;
230 void CPulseAudioClient::__streamUnderflowCb(pa_stream* s, void* user_data) {
234 CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
236 AUDIO_IO_LOGD("stream %p UnderFlow...", pClient);
238 pClient->__mIsStarted = false;
241 void CPulseAudioClient::__streamEventCb(pa_stream* s, const char *name, pa_proplist *pl, void *user_data) {
245 AUDIO_IO_LOGE("NAME : %s, Prop : %p", name, pl);
247 CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
248 if (strcmp(name, PA_STREAM_EVENT_POP_TIMEOUT) == 0) {
249 pa_operation_unref(pa_stream_cork(pClient->__mpStream, 1, NULL, NULL));
254 void CPulseAudioClient::__successStreamCb(pa_stream* s, int success, void* user_data) {
255 AUDIO_IO_LOGD("pa_stream[%p], success[%d], user_data[%p]", s, success, user_data);
259 CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
260 pClient->__mIsOperationSuccess = static_cast<bool>(success);
262 pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
265 void CPulseAudioClient::__successDrainCbInThread(pa_stream* s, int success, void* user_data) {
266 AUDIO_IO_LOGD("pa_stream[%p], success[%d], user_data[%p]", s, success, user_data);
270 CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
271 pClient->__mIsOperationSuccess = static_cast<bool>(success);
272 pClient->__mIsDraining = false;
275 void CPulseAudioClient::__successDrainCb(pa_stream* s, int success, void* user_data) {
276 AUDIO_IO_LOGD("pa_stream[%p], success[%d], user_data[%p]", s, success, user_data);
280 CPulseAudioClient* pClient = static_cast<CPulseAudioClient*>(user_data);
281 pClient->__mIsOperationSuccess = static_cast<bool>(success);
282 pClient->__mIsDraining = false;
284 pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
287 void CPulseAudioClient::initialize() {
288 if (__mIsInit == true) {
296 // Allocates PA proplist
297 __mpPropList = pa_proplist_new();
298 if (__mpPropList == NULL) {
299 THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_proplist_new()");
302 // Adds values on proplist for delivery to PULSEAUDIO
303 char *streamType = NULL;
304 CAudioInfo::EAudioType audioType = __mSpec.getAudioInfo().getAudioType();
305 __mSpec.getAudioInfo().convertAudioType2StreamType(audioType, &streamType);
306 pa_proplist_sets(__mpPropList, PA_PROP_MEDIA_ROLE, streamType);
308 int index = __mSpec.getAudioInfo().getAudioIndex();
310 pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_PARENT_ID, "%u", (unsigned int) index);
313 // Adds latency on proplist for delivery to PULSEAUDIO
314 AUDIO_IO_LOGD("LATENCY : %s[%d]", __mSpec.getStreamLatencyToString(), static_cast<int>(__mSpec.getStreamLatency()));
315 pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_TIZEN_AUDIO_LATENCY, "%s", __mSpec.getStreamLatencyToString());
317 // Allocates PA mainloop
318 __mpMainloop = pa_threaded_mainloop_new();
319 if (__mpMainloop == NULL) {
320 THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_threaded_mainloop_new()");
323 // Allocates PA context
324 __mpContext = pa_context_new(pa_threaded_mainloop_get_api(__mpMainloop), CLIENT_NAME);
325 if (__mpContext == NULL) {
326 THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_context_new()");
329 // Sets context state changed callback
330 pa_context_set_state_callback(__mpContext, __contextStateChangeCb, this);
332 // Connects this client with PA server
333 if (pa_context_connect(__mpContext, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0) {
334 THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_context_connect()");
337 // LOCK for synchronous connection
338 pa_threaded_mainloop_lock(__mpMainloop);
341 if (pa_threaded_mainloop_start(__mpMainloop) < 0) {
342 pa_threaded_mainloop_unlock(__mpMainloop);
343 THROW_ERROR_MSG(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_threaded_mainloop_start()");
346 // Connection process is asynchronously
347 // So, this function will be waited when occurred context state change event
348 // If I got a signal, do next processing
350 pa_context_state_t state;
351 state = pa_context_get_state(__mpContext);
353 if (state == PA_CONTEXT_READY) {
357 if (!PA_CONTEXT_IS_GOOD(state)) {
358 err = pa_context_errno(__mpContext);
359 pa_threaded_mainloop_unlock(__mpMainloop);
360 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "pa_context's state is not good : err[%d]", err);
363 /* Wait until the context is ready */
364 pa_threaded_mainloop_wait(__mpMainloop);
367 // Allocates PA stream
368 pa_sample_spec ss = __mSpec.getSampleSpec();
369 pa_channel_map map = __mSpec.getChannelMap();
371 __mpStream = pa_stream_new_with_proplist(__mpContext, __mSpec.getStreamName(), &ss, &map, __mpPropList);
372 if (__mpStream == NULL) {
373 pa_threaded_mainloop_unlock(__mpMainloop);
374 THROW_ERROR_MSG(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_new_with_proplist()");
377 // Sets stream callbacks
378 pa_stream_set_state_callback(__mpStream, __streamStateChangeCb, this);
379 pa_stream_set_read_callback(__mpStream, __streamCaptureCb, this);
380 pa_stream_set_write_callback(__mpStream, __streamPlaybackCb, this);
381 pa_stream_set_latency_update_callback(__mpStream, __streamLatencyUpdateCb, this);
382 pa_stream_set_event_callback(__mpStream, __streamEventCb, this);
383 if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
384 pa_stream_set_started_callback(__mpStream, __streamStartedCb, this);
385 pa_stream_set_underflow_callback(__mpStream, __streamUnderflowCb, this);
388 // Connect stream with PA Server
390 if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
391 pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(
392 PA_STREAM_INTERPOLATE_TIMING |
393 PA_STREAM_ADJUST_LATENCY |
394 #ifndef DISABLE_MOBILE_BACK_COMP
395 PA_STREAM_START_CORKED |
397 PA_STREAM_AUTO_TIMING_UPDATE);
399 ret = pa_stream_connect_playback(__mpStream, NULL, NULL, flags, NULL, NULL);
401 pa_stream_flags_t flags = static_cast<pa_stream_flags_t>(
402 PA_STREAM_INTERPOLATE_TIMING |
403 PA_STREAM_ADJUST_LATENCY |
404 #ifndef DISABLE_MOBILE_BACK_COMP
405 PA_STREAM_START_CORKED |
407 PA_STREAM_AUTO_TIMING_UPDATE);
409 ret = pa_stream_connect_record(__mpStream, NULL, NULL, flags);
413 err = pa_context_errno(__mpContext);
414 pa_threaded_mainloop_unlock(__mpMainloop);
415 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_connect() : err[%d]", err);
419 pa_stream_state_t state;
420 state = pa_stream_get_state(__mpStream);
422 if (state == PA_STREAM_READY) {
423 AUDIO_IO_LOGD("STREAM READY");
427 if (!PA_STREAM_IS_GOOD(state)) {
428 err = pa_context_errno(__mpContext);
429 pa_threaded_mainloop_unlock(__mpMainloop);
430 if (err == PA_ERR_ACCESS) {
431 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_DEVICE_POLICY_RESTRICTION, "pa_stream's state is not good : err[%d]", err);
433 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "pa_stream's state is not good : err[%d]", err);
437 /* Wait until the stream is ready */
438 pa_threaded_mainloop_wait(__mpMainloop);
441 // End of synchronous
442 pa_threaded_mainloop_unlock(__mpMainloop);
444 // __mIsInit = true; // Moved to __streamStateChangeCb()
445 } catch (CAudioError& e) {
451 void CPulseAudioClient::finalize() {
454 if (__mpMainloop && isInThread() == false)
455 pa_threaded_mainloop_lock(__mpMainloop);
457 /* clear callbacks */
459 if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK)
460 pa_stream_set_write_callback(__mpStream, NULL, NULL);
462 pa_stream_set_read_callback(__mpStream, NULL, NULL);
463 pa_stream_set_latency_update_callback(__mpStream, NULL, NULL);
464 pa_stream_set_event_callback(__mpStream, NULL, NULL);
467 if (__mpMainloop && isInThread() == false)
468 pa_threaded_mainloop_unlock(__mpMainloop);
470 /* Wait for drain complete if draining before finalize */
472 unsigned int drain_wait_count = 0;
473 while (__mIsDraining && drain_wait_count++ < drain_wait_max_count) {
474 usleep(drain_wait_interval);
476 AUDIO_IO_LOGD("wait for drain complete!!!! [%d * %d usec]",
477 drain_wait_count, drain_wait_interval);
480 if (__mpMainloop != NULL) {
481 pa_threaded_mainloop_stop(__mpMainloop);
484 if (__mpStream != NULL) {
485 pa_stream_disconnect(__mpStream);
486 pa_stream_unref(__mpStream);
490 if (__mpContext != NULL) {
491 pa_context_disconnect(__mpContext);
492 pa_context_unref(__mpContext);
496 if (__mpMainloop != NULL) {
497 pa_threaded_mainloop_free(__mpMainloop);
501 if (__mpPropList != NULL) {
502 pa_proplist_free(__mpPropList);
509 int CPulseAudioClient::read(void* buffer, size_t length) {
510 if (__mIsInit == false) {
511 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
516 if (buffer == NULL) {
517 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "The parameter is invalid : buffer[%p]", buffer);
520 if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
521 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function");
524 size_t lengthIter = length;
527 __mIsUsedSyncRead = true;
530 pa_threaded_mainloop_lock(__mpMainloop);
532 while (lengthIter > 0) {
535 while (__mpSyncReadDataPtr == NULL) {
536 ret = pa_stream_peek(__mpStream, &__mpSyncReadDataPtr, &__mSyncReadLength);
538 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pa_stream_peek() : ret[%d]", ret);
541 if (__mSyncReadLength <= 0) {
542 #ifdef _AUDIO_IO_DEBUG_TIMING_
543 AUDIO_IO_LOGD("readLength(%zu byte) is not valid. wait...", __mSyncReadLength);
545 pa_threaded_mainloop_wait(__mpMainloop);
546 } else if (__mpSyncReadDataPtr == NULL) {
547 // Data peeked, but it doesn't have any data
548 ret = pa_stream_drop(__mpStream);
550 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pa_stream_drop() : ret[%d]", ret);
553 __mSyncReadIndex = 0;
557 if (__mSyncReadLength < lengthIter) {
558 l = __mSyncReadLength;
563 // Copy partial pcm data on out parameter
564 #ifdef _AUDIO_IO_DEBUG_TIMING_
565 AUDIO_IO_LOGD("memcpy() that a peeked buffer[%p], index[%zu], length[%zu] on out buffer", (const uint8_t*)(__mpSyncReadDataPtr) + __mSyncReadIndex, __mSyncReadIndex, l);
567 memcpy(buffer, (const uint8_t*)__mpSyncReadDataPtr + __mSyncReadIndex, l);
569 // Move next position
570 buffer = (uint8_t*)buffer + l;
573 // Adjusts the rest length
574 __mSyncReadIndex += l;
575 __mSyncReadLength -= l;
577 if (__mSyncReadLength == 0) {
578 #ifdef _AUDIO_IO_DEBUG_TIMING_
579 AUDIO_IO_LOGD("__mSyncReadLength is zero, do drop()");
581 ret = pa_stream_drop(__mpStream);
583 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pa_stream_drop() : ret[%d]", ret);
586 // Reset the internal pointer
587 __mpSyncReadDataPtr = NULL;
588 __mSyncReadLength = 0;
589 __mSyncReadIndex = 0;
591 } // End of while (lengthIter > 0)
593 pa_threaded_mainloop_unlock(__mpMainloop);
594 __mIsUsedSyncRead = false;
595 } catch (CAudioError& e) {
596 pa_threaded_mainloop_unlock(__mpMainloop);
597 __mIsUsedSyncRead = false;
604 int CPulseAudioClient::peek(const void** buffer, size_t* length) {
605 if (__mIsInit == false) {
606 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
611 if (buffer == NULL || length == NULL) {
612 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "The parameter is invalid : buffer[%p], length[%p]", buffer, length);
615 if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
616 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function");
621 if (isInThread() == false) {
622 pa_threaded_mainloop_lock(__mpMainloop);
623 ret = pa_stream_peek(__mpStream, buffer, length);
624 pa_threaded_mainloop_unlock(__mpMainloop);
626 ret = pa_stream_peek(__mpStream, buffer, length);
629 #ifdef _AUDIO_IO_DEBUG_TIMING_
630 AUDIO_IO_LOGD("buffer[%p], length[%zu]", *buffer, *length);
634 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_peek() : err[%d]", ret);
640 int CPulseAudioClient::drop() {
641 if (__mIsInit == false) {
642 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
645 #ifdef _AUDIO_IO_DEBUG_TIMING_
651 if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
652 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function");
657 if (isInThread() == false) {
658 pa_threaded_mainloop_lock(__mpMainloop);
659 ret = pa_stream_drop(__mpStream);
660 pa_threaded_mainloop_unlock(__mpMainloop);
662 ret = pa_stream_drop(__mpStream);
666 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_drop() : err[%d]", ret);
672 int CPulseAudioClient::write(const void* data, size_t length) {
674 THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_ARGUMENT, "The parameter is invalid");
677 if (__mDirection == EStreamDirection::STREAM_DIRECTION_RECORD) {
678 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "The Playback client couldn't use this function");
683 #ifdef _AUDIO_IO_DEBUG_TIMING_
684 AUDIO_IO_LOGD("data[%p], length[%zu], First[%d]", data, length, __mIsFirstStream);
687 if (isInThread() == false) {
688 pa_threaded_mainloop_lock(__mpMainloop);
689 if (pa_stream_is_corked(__mpStream)) {
690 AUDIO_IO_LOGW("stream is corked...do uncork here first!!!!");
691 pa_operation_unref(pa_stream_cork(__mpStream, 0, NULL, this));
694 ret = pa_stream_write(__mpStream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
695 pa_threaded_mainloop_unlock(__mpMainloop);
697 if (pa_stream_is_corked(__mpStream)) {
698 AUDIO_IO_LOGW("stream is corked...do uncork here first!!!!");
699 pa_operation_unref(pa_stream_cork(__mpStream, 0, NULL, this));
702 if (__mIsFirstStream) {
703 const pa_buffer_attr* attr = pa_stream_get_buffer_attr(__mpStream);
704 uint32_t prebuf = attr->prebuf;
705 // Compensate amount of prebuf in first stream callback when audio-io use prebuf(-1)
706 // Because a stream will never start when an application wrote less than prebuf at first
707 if (length < prebuf) {
708 char* dummy = new char[prebuf - length];
709 memset(dummy, 0, prebuf - length);
710 pa_stream_write(__mpStream, dummy, prebuf - length, NULL, 0LL, PA_SEEK_RELATIVE);
713 __mIsFirstStream = false;
714 AUDIO_IO_LOGW("FIRST STREAM CALLBACK : length[%zu], prebuf[%d], dummy[%zu]",
715 length, prebuf, (length < prebuf) ? prebuf - length : 0);
717 ret = pa_stream_write(__mpStream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
721 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_write() : err[%d]", ret);
727 void CPulseAudioClient::cork(bool cork) {
728 AUDIO_IO_LOGD("cork[%d]", cork);
730 if (__mIsInit == false) {
731 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
734 if (isInThread() == true) {
735 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This operation is not supported in callback");
740 // Set __mIsFirstStream flag when uncork(resume) stream, because prebuf will be enable again
742 __mIsFirstStream = true;
744 if (isInThread() == false) {
745 pa_threaded_mainloop_lock(__mpMainloop);
746 pa_operation_unref(pa_stream_cork(__mpStream, static_cast<int>(cork), __successStreamCb, this));
747 pa_threaded_mainloop_unlock(__mpMainloop);
749 pa_operation_unref(pa_stream_cork(__mpStream, static_cast<int>(cork), __successStreamCb, this));
755 bool CPulseAudioClient::isCorked() {
756 if (__mIsInit == false) {
757 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
764 if (isInThread() == false) {
765 pa_threaded_mainloop_lock(__mpMainloop);
766 isCork = pa_stream_is_corked(__mpStream);
767 pa_threaded_mainloop_unlock(__mpMainloop);
769 isCork = pa_stream_is_corked(__mpStream);
772 #ifdef _AUDIO_IO_DEBUG_TIMING_
773 AUDIO_IO_LOGD("isCork[%d]", isCork);
775 return static_cast<bool>(isCork);
778 bool CPulseAudioClient::drain() {
779 if (__mIsInit == false) {
780 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
786 AUDIO_IO_LOGW("Corked...");
791 AUDIO_IO_LOGW("already draining...");
796 AUDIO_IO_LOGW("stream not started yet...skip drain");
800 if (isInThread() == false) {
801 AUDIO_IO_LOGD("drain");
802 pa_threaded_mainloop_lock(__mpMainloop);
803 pa_operation* o = pa_stream_drain(__mpStream, __successDrainCb, this);
804 __mIsDraining = true;
805 while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
806 pa_threaded_mainloop_wait(__mpMainloop);
808 pa_operation_unref(o);
809 pa_threaded_mainloop_unlock(__mpMainloop);
810 AUDIO_IO_LOGD("drain done");
812 AUDIO_IO_LOGD("drain in thread");
813 pa_operation_unref(pa_stream_drain(__mpStream, __successDrainCbInThread, this));
814 __mIsDraining = true;
820 bool CPulseAudioClient::flush() {
821 if (__mIsInit == false) {
822 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
827 if (isInThread() == false) {
828 AUDIO_IO_LOGD("flush");
829 pa_threaded_mainloop_lock(__mpMainloop);
830 pa_operation_unref(pa_stream_flush(__mpStream, __successStreamCb, this));
831 pa_threaded_mainloop_unlock(__mpMainloop);
833 AUDIO_IO_LOGD("flush in thread");
834 pa_operation_unref(pa_stream_flush(__mpStream, __successStreamCb, this));
840 size_t CPulseAudioClient::getWritableSize() {
841 if (__mIsInit == false) {
842 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
847 if (__mDirection != EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
848 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This client is used for Playback");
853 if (isInThread() == false) {
854 pa_threaded_mainloop_lock(__mpMainloop);
855 ret = pa_stream_writable_size(__mpStream);
856 pa_threaded_mainloop_unlock(__mpMainloop);
858 ret = pa_stream_writable_size(__mpStream);
864 void CPulseAudioClient::checkRunningState() {
865 if (__mpContext == NULL || PA_CONTEXT_IS_GOOD(pa_context_get_state(__mpContext)) == 0) {
866 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_INITIALIZED, "The context[%p] is not created or not good state", __mpContext);
868 if (__mpStream == NULL || PA_STREAM_IS_GOOD(pa_stream_get_state(__mpStream)) == 0) {
869 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_INITIALIZED, "The stream[%p] is not created or not good state", __mpStream);
871 if (pa_context_get_state(__mpContext) != PA_CONTEXT_READY || pa_stream_get_state(__mpStream) != PA_STREAM_READY) {
872 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_INITIALIZED, "The context[%p] or stream[%p] state is not ready", __mpContext, __mpStream);
875 #ifdef _AUDIO_IO_DEBUG_TIMING_
876 AUDIO_IO_LOGD("This client is running");
880 bool CPulseAudioClient::isInThread() {
881 int ret = pa_threaded_mainloop_in_thread(__mpMainloop);
883 #ifdef _AUDIO_IO_DEBUG_TIMING_
884 AUDIO_IO_LOGD("isInThread[%d]", ret);
886 return static_cast<bool>(ret);
889 size_t CPulseAudioClient::getReadableSize() {
890 if (__mIsInit == false) {
891 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
896 if (__mDirection != EStreamDirection::STREAM_DIRECTION_RECORD) {
897 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "This client is used for Capture");
902 if (isInThread() == false) {
903 pa_threaded_mainloop_lock(__mpMainloop);
904 ret = pa_stream_writable_size(__mpStream);
905 pa_threaded_mainloop_unlock(__mpMainloop);
907 ret = pa_stream_writable_size(__mpStream);
913 size_t CPulseAudioClient::getBufferSize() {
914 if (__mIsInit == false) {
915 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
923 if (isInThread() == false) {
924 pa_threaded_mainloop_lock(__mpMainloop);
927 const pa_buffer_attr* attr = pa_stream_get_buffer_attr(__mpStream);
929 int _err = pa_context_errno(__mpContext);
930 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_buffer_attr() : err[%d]", _err);
933 if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
935 AUDIO_IO_LOGD("PLAYBACK buffer size[%zu]", ret);
937 ret = attr->fragsize;
938 AUDIO_IO_LOGD("RECORD buffer size[%zu]", ret);
940 } catch (CAudioError& e) {
941 if (isInThread() == false) {
942 pa_threaded_mainloop_unlock(__mpMainloop);
947 if (isInThread() == false) {
948 pa_threaded_mainloop_unlock(__mpMainloop);
954 pa_usec_t CPulseAudioClient::getLatency() {
955 if (__mIsInit == false) {
956 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
964 if (isInThread() == false) {
965 if (pa_stream_get_latency(__mpStream, &ret, &negative) < 0) {
966 int _err = pa_context_errno(__mpContext);
967 if (_err != PA_ERR_NODATA) {
968 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_latency() : err[%d]", _err);
971 return negative ? 0 : ret;
974 pa_threaded_mainloop_lock(__mpMainloop);
978 if (pa_stream_get_latency(__mpStream, &ret, &negative) >= 0) {
982 int _err = pa_context_errno(__mpContext);
983 if (_err != PA_ERR_NODATA) {
984 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed pa_stream_get_latency() : err[%d]", _err);
987 /* Wait until latency data is available again */
988 pa_threaded_mainloop_wait(__mpMainloop);
990 } catch (CAudioError& e) {
991 pa_threaded_mainloop_unlock(__mpMainloop);
995 pa_threaded_mainloop_unlock(__mpMainloop);
997 return negative ? 0 : ret;
1000 pa_usec_t CPulseAudioClient::getFinalLatency() {
1001 if (__mIsInit == false) {
1002 THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
1005 checkRunningState();
1011 if (isInThread() == false) {
1012 pa_threaded_mainloop_lock(__mpMainloop);
1015 ver = pa_context_get_server_protocol_version(__mpContext);
1017 const pa_buffer_attr* buffer_attr = pa_stream_get_buffer_attr(__mpStream);
1018 const pa_sample_spec* sample_spec = pa_stream_get_sample_spec(__mpStream);
1019 const pa_timing_info* timing_info = pa_stream_get_timing_info(__mpStream);
1021 if (buffer_attr == NULL || sample_spec == NULL || timing_info == NULL) {
1022 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",
1023 buffer_attr, sample_spec, timing_info);
1026 if (__mDirection == EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
1027 ret = (pa_bytes_to_usec(buffer_attr->tlength, sample_spec) + timing_info->configured_sink_usec);
1028 AUDIO_IO_LOGD("FINAL PLAYBACK LATENCY[%" PRIu64 "]", ret);
1030 ret = (pa_bytes_to_usec(buffer_attr->fragsize, sample_spec) + timing_info->configured_source_usec);
1031 AUDIO_IO_LOGD("FINAL RECORD LATENCY[%" PRIu64 "]", ret);
1034 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_SUPPORTED, "This version(ver.%d) is not supported", ver);
1037 if (isInThread() == false) {
1038 pa_threaded_mainloop_unlock(__mpMainloop);
1040 } catch (CAudioError& e) {
1041 if (isInThread() == false) {
1042 pa_threaded_mainloop_unlock(__mpMainloop);
1050 CPulseAudioClient::EStreamDirection CPulseAudioClient::getStreamDirection() {
1051 return __mDirection;
1054 CPulseStreamSpec CPulseAudioClient::getStreamSpec() {