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