Move drain method to subclass CAudioOutput
[platform/core/api/audio-io.git] / src / cpp / CAudioOutput.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 <new>
19
20 #include "CAudioIODef.h"
21 #include <sched.h>
22
23 using namespace std;
24 using namespace tizen_media_audio;
25
26 /**
27  * class CAudioOutput
28  */
29
30 CAudioOutput::CAudioOutput(CAudioInfo& info) :
31     CAudioIO(info),
32     __mIsUsedSyncWrite(false),
33     __mIsInit(false) {
34     mDirection = CAudioInfo::EAudioDirection::AUDIO_DIRECTION_OUT;
35 }
36
37 void CAudioOutput::onStream(CPulseAudioClient* pClient, size_t length) {
38     assert(pClient);
39
40     /*
41      * Does not call CAudioIO::onStream() for synchronization
42      * if a user is using write()
43      */
44     if (__mIsUsedSyncWrite) {
45 #ifdef _AUDIO_IO_DEBUG_TIMING_
46         AUDIO_IO_LOGD("Sync Write Mode! - signal! - pClient:[%p], length:[%zu]", pClient, length);
47 #endif
48         internalSignal();
49         return;
50     }
51
52     /*
53      * Accrues callback function
54      */
55 #ifdef _AUDIO_IO_DEBUG_TIMING_
56     AUDIO_IO_LOGD("pClient:[%p], length:[%zu]", pClient, length);
57 #endif
58     CAudioIO::onStream(pClient, length);
59 }
60
61 void CAudioOutput::__setInit(bool flag) noexcept {
62     __mIsInit = flag;
63 }
64
65 bool CAudioOutput::__IsInit() noexcept {
66     return (CAudioIO::isInit() && __mIsInit);
67 }
68
69 bool CAudioOutput::__IsReady() noexcept {
70     return CAudioIO::IsReady();
71 }
72
73 void CAudioOutput::initialize() {
74     if (__IsInit())
75         return;
76
77     try {
78         CAudioIO::initialize();
79         __setInit(true);
80     } catch (const CAudioError& e) {
81 //LCOV_EXCL_START
82         finalize();
83         throw;
84 //LCOV_EXCL_STOP
85     }
86
87     CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
88 }
89
90 void CAudioOutput::finalize() {
91     if (!__IsInit()) {
92         AUDIO_IO_LOGD("Did not initialize");
93         return;
94     }
95
96     CAudioIO::finalize();
97
98     __setInit(false);
99 }
100
101 void CAudioOutput::prepare() {
102     if (!__IsInit())
103         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioOutput"); //LCOV_EXCL_LINE
104
105     if (__IsReady()) {
106 //LCOV_EXCL_START
107         AUDIO_IO_LOGD("Already prepared CAudioOutput");
108         CAudioIO::prepare();
109         return;
110 //LCOV_EXCL_STOP
111     }
112
113     /* Check invalid AudioType */
114     CAudioInfo::EAudioType audioType = mAudioInfo.getAudioType();
115     if (audioType < CAudioInfo::EAudioType::AUDIO_OUT_TYPE_MEDIA ||
116         audioType >= CAudioInfo::EAudioType::AUDIO_TYPE_MAX)
117         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT,
118                                "The audioType is invalid [type:%d]", static_cast<int>(audioType));
119
120     try {
121         /* Init StreamSpec */
122         CPulseStreamSpec::EStreamLatency streamSpec = CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_OUTPUT_DEFAULT;
123 #ifndef DISABLE_MOBILE_BACK_COMP
124         if (!mStreamCallback.onStream) {
125             AUDIO_IO_LOGD("Set Stream Spec : CPulseStreamSpec::STREAM_LATENCY_OUTPUT_DEFAULT");
126             streamSpec = CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_OUTPUT_DEFAULT;
127         } else {
128             AUDIO_IO_LOGD("Set Stream Spec : CPulseStreamSpec::STREAM_LATENCY_OUTPUT_DEFAULT_ASYNC");
129             streamSpec = CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_OUTPUT_DEFAULT_ASYNC;
130         }
131 #endif
132         /* Override the default value by audio type */
133         if (audioType == CAudioInfo::EAudioType::AUDIO_OUT_TYPE_VOIP)
134             streamSpec = CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_OUTPUT_VOIP;
135         else if (audioType == CAudioInfo::EAudioType::AUDIO_OUT_TYPE_MEDIA_NETWORK_SOURCE)
136             streamSpec = CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_OUTPUT_HIGH;
137
138         CPulseStreamSpec spec(streamSpec, mAudioInfo);
139
140         internalLock();
141         mpPulseAudioClient = new CPulseAudioClient(CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_PLAYBACK, spec, this);
142         mpPulseAudioClient->initialize();
143 #ifndef DISABLE_MOBILE_BACK_COMP
144         /* Uncork stream which is created with CORKED flag */
145         mpPulseAudioClient->cork(false);
146 #endif
147         internalUnlock();
148
149         CAudioIO::prepare();
150     } catch (const CAudioError& e) {
151 //LCOV_EXCL_START
152         SAFE_FINALIZE(mpPulseAudioClient);
153         SAFE_DELETE(mpPulseAudioClient);
154         internalUnlock();
155         throw;
156 //LCOV_EXCL_STOP
157     } catch (const std::bad_alloc&) {
158 //LCOV_EXCL_START
159         internalUnlock();
160         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed to allocate CPulseAudioClient object");
161 //LCOV_EXCL_STOP
162     }
163 }
164
165 void CAudioOutput::unprepare() {
166     if (!__IsInit())
167         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, //LCOV_EXCL_LINE
168                         "Did not initialize CAudioOutput");         //LCOV_EXCL_LINE
169
170     if (!__IsReady()) {
171         AUDIO_IO_LOGD("Already unprepared");
172         return;
173     }
174
175     CAudioIO::unprepare();
176
177     try {
178         internalLock();
179         if (mpPulseAudioClient && mpPulseAudioClient->isInThread())
180             THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't unprepare inside pulseaudio thread");
181         SAFE_FINALIZE(mpPulseAudioClient);
182         SAFE_DELETE(mpPulseAudioClient);
183         internalUnlock();
184     } catch (const CAudioError& e) {
185 //LCOV_EXCL_START
186         internalUnlock();
187         throw;
188 //LCOV_EXCL_STOP
189     }
190
191     CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
192 }
193
194 void CAudioOutput::pause() {
195     if (!__IsInit() || !__IsReady())
196         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,    //LCOV_EXCL_LINE
197                         "Did not initialize or prepare CAudioOutput"); //LCOV_EXCL_LINE
198
199     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING)
200         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE,
201                         "Can't pause if not in Running state");
202
203     if (mpPulseAudioClient->isInThread() )
204         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't pause in thread"); //LCOV_EXCL_LINE
205
206     CAudioIO::pause();
207     CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
208 }
209
210 void CAudioOutput::resume() {
211     if (!__IsInit() || !__IsReady())
212         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,    //LCOV_EXCL_LINE
213                         "Did not initialize or prepare CAudioOutput"); //LCOV_EXCL_LINE
214
215     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED)
216         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE,
217                         "Can't resume if not in Paused state");
218
219     if (mpPulseAudioClient->isInThread())
220         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't resume in thread"); //LCOV_EXCL_LINE
221
222     CAudioIO::resume();
223     CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
224 }
225
226 void CAudioOutput::drain() {
227     if (!__IsInit() || !__IsReady())
228         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,    //LCOV_EXCL_LINE
229                         "Did not initialize or prepare CAudioOutput"); //LCOV_EXCL_LINE
230
231     if (mStreamCallback.onStream)
232         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "async type don't support drain");
233
234     try {
235         if (mpPulseAudioClient->isInThread()) {
236             mpPulseAudioClient->drain();
237         } else {
238             internalLock();
239             mpPulseAudioClient->drain();
240             internalUnlock();
241         }
242     } catch (const CAudioError& e) {
243         if (!mpPulseAudioClient->isInThread())
244             internalUnlock();
245         throw;
246     }
247 }
248
249 void CAudioOutput::flush() {
250     if (!__IsInit() || !__IsReady())
251         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,    //LCOV_EXCL_LINE
252                         "Did not initialize or prepare CAudioOutput"); //LCOV_EXCL_LINE
253
254     CAudioIO::flush();
255 }
256
257 int CAudioOutput::getBufferSize() {
258     if (!__IsInit())
259         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,    //LCOV_EXCL_LINE
260                         "Did not initialize or prepare CAudioOutput"); //LCOV_EXCL_LINE
261
262     /* FIXME : return calculated size here to satisfy backward compatibility */
263     return (mAudioInfo.getSampleRate() * DEFAULT_PERIOD_SIZE) / 1000 * mAudioInfo.getSampleSize();
264 }
265
266 size_t CAudioOutput::write(const void* buffer, size_t length) {
267     if (!__IsInit() || !__IsReady())
268         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,    //LCOV_EXCL_LINE
269                         "Did not initialize or prepare CAudioOutput"); //LCOV_EXCL_LINE
270
271     if (!buffer)
272         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT,
273                                "Parameters are invalid - buffer:%p, length:%zu", buffer, length);
274
275     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING)
276         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
277                         "Can't write if not in Running state");
278
279     /* When write() is called in PulseAudio callback, bypass a pcm data to CPulseAudioClient (For Asynchronous) */
280     if (mpPulseAudioClient && mpPulseAudioClient->isInThread()) {
281         int ret = mpPulseAudioClient->write(buffer, length);
282         if (ret < 0)
283             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION,
284                                    "The written result is invalid ret:%d", ret);
285 #ifdef _AUDIO_IO_DEBUG_TIMING_
286         AUDIO_IO_LOGD("CPulseAudioClient->write(buffer:%p, length:%zu)", buffer, length);
287 #endif
288         return length;
289     }
290
291     try {
292         /* For synchronization */
293         internalLock();
294
295         // If another thread did call unprepare, do not write
296         if (!mpPulseAudioClient)
297             THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, //LCOV_EXCL_LINE
298                             "Did not initialize CPulseAudioClient");    //LCOV_EXCL_LINE
299
300         // Sets synchronous flag
301         __mIsUsedSyncWrite = true;
302
303         size_t lengthIter = length;
304
305         while (lengthIter > 0) {
306             size_t l;
307
308             while ((l = mpPulseAudioClient->getWritableSize()) == 0) {
309 #ifdef _AUDIO_IO_DEBUG_TIMING_
310                 AUDIO_IO_LOGD("writableSize is [%zu].. wait", l);
311 #endif
312                 internalWait();
313             }
314
315             if (l > lengthIter)
316                 l = lengthIter;
317
318 #ifdef _AUDIO_IO_DEBUG_TIMING_
319             AUDIO_IO_LOGD("CPulseAudioClient->write(buffer:%p, length:%zu)", buffer, l);
320 #endif
321
322             int ret = mpPulseAudioClient->write(buffer, l);
323             if (ret < 0)
324                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION,//LCOV_EXCL_LINE
325                                        "The written result is invalid ret:%d", ret); //LCOV_EXCL_LINE
326
327             buffer = static_cast<const uint8_t*>(buffer) + l;
328             lengthIter -= l;
329         }  // End of while (length > 0)
330
331         __mIsUsedSyncWrite = false;
332         internalUnlock();
333
334         sched_yield();
335     } catch (const CAudioError& e) {
336         __mIsUsedSyncWrite = false;
337         internalUnlock();
338         throw;
339     }
340
341     return length;
342 }