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