CAudioInput/Output: Apply VoIP latency in case of VoIP audio type
[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         /* Override the default value by audio type */
142         if (audioType == CAudioInfo::EAudioType::AUDIO_OUT_TYPE_VOIP)
143             streamSpec = CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_OUTPUT_VOIP;
144
145         CPulseStreamSpec spec(streamSpec, mAudioInfo);
146
147         internalLock();
148         mpPulseAudioClient = new CPulseAudioClient(CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_PLAYBACK, spec, this);
149         mpPulseAudioClient->initialize();
150 #ifndef DISABLE_MOBILE_BACK_COMP
151         /* Uncork stream which is created with CORKED flag */
152         mpPulseAudioClient->cork(false);
153 #endif
154         internalUnlock();
155
156         CAudioIO::prepare();
157     } catch (CAudioError& e) {
158         SAFE_FINALIZE(mpPulseAudioClient);
159         SAFE_DELETE(mpPulseAudioClient);
160         internalUnlock();
161         throw;
162     } catch (const std::bad_alloc&) {
163         internalUnlock();
164         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed to allocate CPulseAudioClient object");
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,
314                                        "The written result is invalid ret:%d", ret);
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 }