IPulseStreamListener* listener) :
__mDirection(direction),
__mSpec(spec),
- __mpListener(listener),
- __mpMainloop(nullptr),
- __mpContext(nullptr),
- __mpStream(nullptr),
- __mpPropList(nullptr),
- __mIsInit(false),
- __mIsOperationSuccess(false),
- __mpSyncReadDataPtr(nullptr),
- __mSyncReadIndex(0),
- __mSyncReadLength(0),
- __mIsUsedSyncRead(false),
- __mIsFirstStream(false),
- __mIsDraining(false),
- __mIsStarted(false) {
+ __mpListener(listener) {
}
CPulseAudioClient::~CPulseAudioClient() {
AUDIO_IO_LOGW("pClient[%p] pa_stream[%p] : name[%s], proplist[%p]", pClient, s, name, pl);
- if (strcmp(name, PA_STREAM_EVENT_POP_TIMEOUT) == 0) {
- pa_operation* o = pa_stream_cork(pClient->__mpStream, 1, NULL, NULL);
- if (!o) {
- AUDIO_IO_LOGE("pa_stream[%p] : cork failed", pClient->__mpStream);
- return;
- }
- pa_operation_unref(o);
- }
+ if (strcmp(name, PA_STREAM_EVENT_POP_TIMEOUT) != 0)
+ return;
+
+ if (!pClient->streamCork(true, __successCorkCb, pClient))
+ AUDIO_IO_LOGE("pa_stream[%p] : cork failed", pClient->__mpStream);
}
//LCOV_EXCL_STOP
+void CPulseAudioClient::notifyCorkStatus() {
+ assert(__mpListener);
+ assert(__mpStream);
+
+ __mpListener->onCorked(this, static_cast<bool>(pa_stream_is_corked(__mpStream)));
+}
+
+void CPulseAudioClient::__successCorkCb(pa_stream* s, int success, void* user_data) {
+ assert(s);
+ assert(user_data);
+
+ auto pClient = static_cast<CPulseAudioClient*>(user_data);
+
+ AUDIO_IO_LOGD("pClient[%p] pa_stream[%p] success[%d] user_data[%p]", pClient, s, success, user_data);
+
+ if (success)
+ pClient->notifyCorkStatus();
+}
+
+void CPulseAudioClient::__successCorkCbSignal(pa_stream* s, int success, void* user_data) {
+ assert(s);
+ assert(user_data);
+
+ auto pClient = static_cast<CPulseAudioClient*>(user_data);
+
+ AUDIO_IO_LOGD("pClient[%p] pa_stream[%p] success[%d] user_data[%p]", pClient, s, success, user_data);
+
+ if (success)
+ pClient->notifyCorkStatus();
+
+ pClient->__mIsOperationSuccess = static_cast<bool>(success);
+
+ /* FIXME : verify following action without any waiting */
+ pa_threaded_mainloop_signal(pClient->__mpMainloop, 0);
+}
+
void CPulseAudioClient::__successStreamCb(pa_stream* s, int success, void* user_data) {
assert(s);
assert(user_data);
THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pa_proplist_new()"); //LCOV_EXCL_LINE
// Adds values on proplist for delivery to PULSEAUDIO
- pa_proplist_sets(__mpPropList, PA_PROP_MEDIA_ROLE, __mSpec.getAudioInfo().getConvertedStreamType());
+ auto [ streamType, streamIndex ] = __mSpec.getAudioInfo().getStreamProperties();
- int index = __mSpec.getAudioInfo().getAudioIndex();
- if (index >= 0)
- pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_PARENT_ID, "%u", (unsigned int) index);
+ pa_proplist_sets(__mpPropList, PA_PROP_MEDIA_ROLE, streamType);
+ if (streamIndex >= 0)
+ pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_PARENT_ID, "%d", streamIndex);
if (__mDirection == EStreamDirection::STREAM_DIRECTION_RECORD) {
- int reference_id = __mSpec.getAudioInfo().getEchoCancelReferenceDeviceId();
- if (reference_id > 0) {
- pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_ECHO_CANCEL_REFERENCE_DEVICE, "%d", reference_id);
- }
+ /* Noise-suppression effect should be set first. */
+ std::string ns_method = __mSpec.getAudioInfo().getEffectNoiseSuppressionMethod();
+ auto [ method_reference, device_id ] = __mSpec.getAudioInfo().getEffectMethodWithReference();
+ std::string agc_method = __mSpec.getAudioInfo().getEffectAutomaticGainControlMethod();
- if (!__mSpec.getAudioInfo().getProcessorProperty().empty())
- pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_PREPROCESSOR_METHOD, "%s",
- __mSpec.getAudioInfo().getProcessorProperty().c_str());
+ std::string method_all = ns_method + method_reference + agc_method;
+
+ if (!method_all.empty()) {
+ pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_PREPROCESSOR_METHOD, "%s", method_all.c_str());
+ pa_proplist_setf(__mpPropList, PA_PROP_MEDIA_ECHO_CANCEL_REFERENCE_DEVICE, "%d", device_id);
+
+ AUDIO_IO_LOGD("preprocess will be set. method[%s], device_id[%d]", method_all.c_str(), device_id);
+ }
}
// Adds latency on proplist for delivery to PULSEAUDIO
CPulseThreadLocker locker{__mpMainloop};
if (pa_stream_is_corked(__mpStream)) {
AUDIO_IO_LOGW("pa_stream[%p] is corked...do uncork here first!!!!", __mpStream);
- pa_operation* o = pa_stream_cork(__mpStream, 0, NULL, NULL);
- if (!o)
+ if (!streamCork(false, __successCorkCb, this))
AUDIO_IO_LOGE("pa_stream[%p] : uncork failed", __mpStream);
- else
- pa_operation_unref(o);
}
ret = pa_stream_write(__mpStream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
// LCOV_EXCL_START
if (pa_stream_is_corked(__mpStream)) {
AUDIO_IO_LOGW("pa_stream[%p] is corked...do uncork here first!!!!", __mpStream);
- pa_operation* o = pa_stream_cork(__mpStream, 0, NULL, NULL);
- if (!o)
+
+ if (!streamCork(false, __successCorkCb, this))
AUDIO_IO_LOGE("pa_stream[%p] : uncork failed", __mpStream);
- else
- pa_operation_unref(o);
}
if (__mIsFirstStream) {
return ret;
}
+bool CPulseAudioClient::streamCork(bool cork, pa_stream_success_cb_t cb, void* user_data) {
+ pa_operation* o = pa_stream_cork(__mpStream, static_cast<int>(cork), cb, user_data);
+ if (!o)
+ return false;
+ pa_operation_unref(o);
+
+ return true;
+}
+
void CPulseAudioClient::cork(bool cork) {
AUDIO_IO_LOGD("cork[%d]", cork);
CPulseThreadLocker locker{__mpMainloop};
/* FIXME: wait for completion like drain? */
- pa_operation* o = pa_stream_cork(__mpStream, static_cast<int>(cork), __successStreamCb, this);
- if (!o) {
+ if (!streamCork(cork, __successCorkCbSignal, this)) {
AUDIO_IO_LOGE("pa_stream[%p] : cork[%d] failed", __mpStream, cork);
return;
}
- pa_operation_unref(o);
- AUDIO_IO_LOGD("cork[%d] done", cork);
+
+ AUDIO_IO_LOGD("cork[%d] requested, is-stream-started[%d]", cork, __mIsStarted);
+
+ /* FIXME: need to consider manual pa_stream_trigger() when uncorking but stream is not started yet,
+ because even uncorked here, it is still prebuf state so, empty-pop will not be checked in this situation */
}
bool CPulseAudioClient::isCorked() {