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