Merge branch 'tizen' into tizen_line_coverage
[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:[%zu]", 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:[%zu]", 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         /* Override the default value by audio type */
144         if (audioType == CAudioInfo::EAudioType::AUDIO_OUT_TYPE_VOIP)
145             streamSpec = CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_OUTPUT_VOIP;
146
147         CPulseStreamSpec spec(streamSpec, mAudioInfo);
148
149         internalLock();
150         mpPulseAudioClient = new CPulseAudioClient(CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_PLAYBACK, spec, this);
151         mpPulseAudioClient->initialize();
152 #ifndef DISABLE_MOBILE_BACK_COMP
153         /* Uncork stream which is created with CORKED flag */
154         mpPulseAudioClient->cork(false);
155 #endif
156         internalUnlock();
157
158         CAudioIO::prepare();
159     } catch (CAudioError& e) {
160         SAFE_FINALIZE(mpPulseAudioClient);
161         SAFE_DELETE(mpPulseAudioClient);
162         internalUnlock();
163         throw;
164     } catch (const std::bad_alloc&) {
165 //LCOV_EXCL_START
166         internalUnlock();
167         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed to allocate CPulseAudioClient object");
168 //LCOV_EXCL_STOP
169     }
170 }
171
172 void CAudioOutput::unprepare() {
173     if (__IsInit() == false)
174         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
175                         "Did not initialize CAudioOutput");
176
177     if (__IsReady() == false) {
178         AUDIO_IO_LOGD("Already unprepared");
179         return;
180     }
181
182     CAudioIO::unprepare();
183
184     try {
185         internalLock();
186         if (mpPulseAudioClient && mpPulseAudioClient->isInThread())
187             THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't unprepare inside pulseaudio thread");
188         SAFE_FINALIZE(mpPulseAudioClient);
189         SAFE_DELETE(mpPulseAudioClient);
190         internalUnlock();
191     } catch (CAudioError& e) {
192         internalUnlock();
193         throw;
194     }
195
196     CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
197 }
198
199 void CAudioOutput::pause() {
200     if (__IsInit() == false || __IsReady() == false)
201         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
202                         "Did not initialize or prepare CAudioOutput");
203
204     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING)
205         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE,
206                         "Can't pause if not in Running state");
207
208     if (mpPulseAudioClient->isInThread() == true)
209         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't pause in thread");
210
211     CAudioIO::pause();
212     CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
213 }
214
215 void CAudioOutput::resume() {
216     if (__IsInit() == false || __IsReady() == false)
217         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
218                         "Did not initialize or prepare CAudioOutput");
219
220     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED)
221         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE,
222                         "Can't resume if not in Paused state");
223
224     if (mpPulseAudioClient->isInThread() == true)
225         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't resume in thread");
226
227     CAudioIO::resume();
228     CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
229 }
230
231 void CAudioOutput::drain() {
232     if (__IsInit() == false || __IsReady() == false)
233         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
234                         "Did not initialize or prepare CAudioOutput");
235
236     if (mStreamCallback.onStream)
237         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "async type don't support drain");
238
239     CAudioIO::drain();
240 }
241
242 void CAudioOutput::flush() {
243     if (__IsInit() == false || __IsReady() == false)
244         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
245                         "Did not initialize or prepare CAudioOutput");
246
247     CAudioIO::flush();
248 }
249
250 int CAudioOutput::getBufferSize() {
251     if (__IsInit() == false)
252         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
253                         "Did not initialize or prepare CAudioOutput");
254
255     /* FIXME : return calculated size here to satisfy backward compatibility */
256     return (mAudioInfo.getSampleRate() * DEFAULT_PERIOD_SIZE) / 1000 * mAudioInfo.getSampleSize();
257 }
258
259 size_t CAudioOutput::write(const void* buffer, size_t length) {
260     if (__IsInit() == false || __IsReady() == false)
261         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
262                         "Did not initialize or prepare CAudioOutput");
263
264     if (buffer == NULL)
265         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT,
266                                "Parameters are invalid - buffer:%p, length:%zu", buffer, length);
267
268     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING)
269         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
270                         "Can't write if not in Running state");
271
272     /* When write() is called in PulseAudio callback, bypass a pcm data to CPulseAudioClient (For Asynchronous) */
273     if (mpPulseAudioClient && mpPulseAudioClient->isInThread() == true) {
274         int ret = mpPulseAudioClient->write(buffer, length);
275         if (ret < 0)
276             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION,
277                                    "The written result is invalid ret:%d", ret);
278 #ifdef _AUDIO_IO_DEBUG_TIMING_
279         AUDIO_IO_LOGD("CPulseAudioClient->write(buffer:%p, length:%zu)", buffer, length);
280 #endif
281         return length;
282     }
283
284     try {
285         /* For synchronization */
286         internalLock();
287
288         // If another thread did call unprepare, do not write
289         if (mpPulseAudioClient == NULL)
290             THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,
291                             "Did not initialize CPulseAudioClient");
292
293         // Sets synchronous flag
294         __mIsUsedSyncWrite = true;
295
296         size_t lengthIter = length;
297
298         while (lengthIter > 0) {
299             size_t l;
300
301             while ((l = mpPulseAudioClient->getWritableSize()) == 0) {
302 #ifdef _AUDIO_IO_DEBUG_TIMING_
303                 AUDIO_IO_LOGD("writableSize is [%zu].. wait", l);
304 #endif
305                 internalWait();
306             }
307
308             if (l > lengthIter)
309                 l = lengthIter;
310
311 #ifdef _AUDIO_IO_DEBUG_TIMING_
312             AUDIO_IO_LOGD("CPulseAudioClient->write(buffer:%p, length:%zu)", buffer, l);
313 #endif
314
315             int ret = mpPulseAudioClient->write(buffer, l);
316             if (ret < 0)
317                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION,//LCOV_EXCL_LINE
318                                        "The written result is invalid ret:%d", ret); //LCOV_EXCL_LINE
319
320             buffer = static_cast<const uint8_t*>(buffer) + l;
321             lengthIter -= l;
322         }  // End of while (length > 0)
323
324         __mIsUsedSyncWrite = false;
325         internalUnlock();
326
327         sched_yield();
328     } catch (CAudioError& e) {
329         __mIsUsedSyncWrite = false;
330         internalUnlock();
331         throw;
332     }
333
334     return length;
335 }