Fix SVACE defects
[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 <vconf.h>
19 #include "CAudioIODef.h"
20
21 using namespace std;
22 using namespace tizen_media_audio;
23
24
25 /**
26  * class CAudioOutput
27  */
28 CAudioOutput::CAudioOutput(CAudioInfo& info) :
29     CAudioIO(info),
30     __mIsUsedSyncWrite(false),
31     __mIsInit(false) {
32 }
33
34 CAudioOutput::CAudioOutput(
35         unsigned int            sampleRate,
36         CAudioInfo::EChannel    channel,
37         CAudioInfo::ESampleType sampleType,
38         CAudioInfo::EAudioType  audioType) :
39     __mIsUsedSyncWrite(false),
40     __mIsInit(false) {
41     mAudioInfo = CAudioInfo(sampleRate, channel, sampleType, audioType, -1);
42 }
43
44 CAudioOutput::~CAudioOutput() {
45 }
46
47 void CAudioOutput::onStream(CPulseAudioClient* pClient, size_t length) {
48     assert(pClient);
49
50     /*
51      * Does not call CAudioIO::onStream() for synchronization
52      * if a user is using write()
53      */
54     if (__mIsUsedSyncWrite == true) {
55 #ifdef _AUDIO_IO_DEBUG_TIMING_
56         AUDIO_IO_LOGD("Sync Write Mode! - signal! - pClient:[%p], length:[%d]", pClient, length);
57 #endif
58         internalSignal();
59         return;
60     }
61
62     /*
63      * Accrues callback function
64      */
65 #ifdef _AUDIO_IO_DEBUG_TIMING_
66     AUDIO_IO_LOGD("pClient:[%p], length:[%d]", pClient, length);
67 #endif
68     CAudioIO::onStream(pClient, length);
69 }
70
71 void CAudioOutput::onInterrupt(CAudioSessionHandler* pHandler, int id, mm_sound_focus_type_e focus_type, mm_sound_focus_state_e state, const char *reason_for_change, const char *additional_info) {
72     assert(pHandler);
73     AUDIO_IO_LOGD("[pHandler:0x%x], [focus_type:%d], [state:%d], [reason_for_change:%s], [additional_info:%s]", pHandler, focus_type, state, reason_for_change, additional_info);
74     CAudioIO::onInterrupt(pHandler, id, focus_type, state, reason_for_change, additional_info);
75 }
76
77 void CAudioOutput::onSignal(CAudioSessionHandler* pHandler, mm_sound_signal_name_t signal, int value) {
78     assert(pHandler);
79     AUDIO_IO_LOGD("[pHandler:0x%x], [signal:%d], [value:%d]", pHandler, signal, value);
80     CAudioIO::onSignal(pHandler, signal, value);
81 }
82
83 void CAudioOutput::__setInit(bool flag) {
84     __mIsInit = flag;
85 }
86
87 bool CAudioOutput::__IsInit() {
88     return (CAudioIO::isInit() == true && __mIsInit == true);
89 }
90
91 bool CAudioOutput::__IsReady() {
92     return CAudioIO::IsReady();
93 }
94
95 void CAudioOutput::initialize() throw (CAudioError) {
96     if (__IsInit() == true) {
97         return;
98     }
99
100     try {
101         CAudioIO::initialize();
102
103         // Create ASM Handler
104         mpAudioSessionHandler = new CAudioSessionHandler(CAudioSessionHandler::EAudioSessionType::AUDIO_SESSION_TYPE_PLAYBACK, mAudioInfo, this);
105         if (mpAudioSessionHandler == NULL) {
106             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed to allocate CAudioSessionHandler object");
107         }
108
109         // Initialize ASM Handler
110         mpAudioSessionHandler->initialize();
111
112         __setInit(true);
113         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
114     } catch (CAudioError err) {
115         finalize();
116         throw err;
117     }
118 }
119
120 void CAudioOutput::finalize() {
121     if (__IsInit() == false) {
122         AUDIO_IO_LOGD("Did not initialize");
123         return;
124     }
125
126     SAFE_FINALIZE(mpAudioSessionHandler);
127     SAFE_DELETE(mpAudioSessionHandler);
128
129     CAudioIO::finalize();
130
131     __setInit(false);
132 }
133
134 void CAudioOutput::prepare() throw (CAudioError) {
135     if (__IsInit() == false) {
136         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioOutput");
137     }
138
139     if (__IsReady() == true) {
140         AUDIO_IO_LOGD("Already prepared CAudioOutput");
141         return;
142     }
143
144     try {
145         internalLock();
146
147         // Check to invalid AudioType
148         CAudioInfo::EAudioType audioType = mAudioInfo.getAudioType();
149         if (audioType < CAudioInfo::EAudioType::AUDIO_OUT_TYPE_MEDIA || audioType >= CAudioInfo::EAudioType::AUDIO_TYPE_MAX) {
150             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "The audioType is invalid [type:%d]", static_cast<int>(audioType));
151         }
152
153         if (mpAudioSessionHandler->getId() < 0) {  //Did not registerSound()
154             if (isForceIgnore() == false) {
155                 // Register ASM Listener
156                 AUDIO_IO_LOGD("Register ASM Listener");
157                 mpAudioSessionHandler->registerSound();
158             }
159         }
160
161         // Init StreamSpec
162         AUDIO_IO_LOGD("Set Stream Spec : CPulseStreamSpec::STREAM_LATENCY_OUTPUT_MID");
163         CPulseStreamSpec::EStreamLatency streamSpec = CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_OUTPUT_MID;
164         CPulseStreamSpec spec(streamSpec, mAudioInfo);
165
166         // Create PulseAudio Handler
167         mpPulseAudioClient = new CPulseAudioClient(CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_PLAYBACK, spec, this);
168         if (mpPulseAudioClient == NULL) {
169             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed to allocate CPulseAudioClient object");
170         }
171
172         // Initialize PulseAudio Handler
173         mpPulseAudioClient->initialize();
174
175         if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
176             /* Updates ASM to PLAYING */
177             mpAudioSessionHandler->updatePlaying();
178         }
179
180         internalUnlock();
181
182         CAudioIO::prepare();
183     } catch (CAudioError e) {
184         internalUnlock();
185         throw e;
186     }
187 }
188
189 void CAudioOutput::unprepare() throw (CAudioError) {
190     if (__IsInit() == false) {
191         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioOutput");
192     }
193
194     if (__IsReady() == false) {
195         AUDIO_IO_LOGD("Already unprepared");
196         return;
197     }
198
199     try {
200         CAudioIO::unprepare();
201
202         internalLock();
203
204         SAFE_FINALIZE(mpPulseAudioClient);
205         SAFE_DELETE(mpPulseAudioClient);
206
207         if (mpAudioSessionHandler->getId() >= 0) {
208             /* Updates ASM to STOP */
209             if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
210                 mpAudioSessionHandler->updateStop();
211             }
212
213             bool isSkip = mpAudioSessionHandler->isSkipSessionEvent();
214             if (isSkip == false) {
215                 mpAudioSessionHandler->unregisterSound();
216             }
217         }
218
219         internalUnlock();
220
221         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
222     } catch (CAudioError e) {
223         internalUnlock();
224         throw e;
225     }
226 }
227
228 void CAudioOutput::pause() throw (CAudioError) {
229     if (__IsInit() == false || __IsReady() == false) {
230         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioOutput");
231     }
232
233     try {
234         CAudioIO::pause();
235
236         internalLock();
237
238         /* Updates ASM to STOP */
239         if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
240             mpAudioSessionHandler->updateStop();
241         }
242
243         internalUnlock();
244
245         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
246     } catch (CAudioError e) {
247         internalUnlock();
248         throw e;
249     }
250 }
251
252 void CAudioOutput::resume() throw (CAudioError) {
253     if (__IsInit() == false || __IsReady() == false) {
254         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioOutput");
255     }
256
257     try {
258         internalLock();
259
260         if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
261
262             /* Updates ASM to PLAYING */
263             mpAudioSessionHandler->updatePlaying();
264         }
265
266         internalUnlock();
267
268         CAudioIO::resume();
269
270         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
271     } catch (CAudioError e) {
272         internalUnlock();
273         throw e;
274     }
275 }
276
277 void CAudioOutput::drain() throw (CAudioError) {
278     if (__IsInit() == false || __IsReady() == false) {
279         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioOutput");
280     }
281
282     try {
283         CAudioIO::drain();
284     } catch (CAudioError e) {
285         throw e;
286     }
287 }
288
289 void CAudioOutput::flush() throw (CAudioError) {
290     if (__IsInit() == false || __IsReady() == false) {
291         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioOutput");
292     }
293
294     try {
295         CAudioIO::flush();
296     } catch (CAudioError e) {
297         throw e;
298     }
299 }
300
301 int CAudioOutput::getBufferSize() throw (CAudioError) {
302     if (__IsInit() == false) {
303         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioOutput");
304     }
305
306     if (__IsReady() == false) {
307         AUDIO_IO_LOGD("Warning: Did not prepare CAudioOutput, then return zero");
308         return 0;
309     }
310
311     int size = 0;
312
313     try {
314         size = mpPulseAudioClient->getBufferSize();
315     } catch (CAudioError err) {
316         throw err;
317     }
318
319     return size;
320 }
321
322 size_t CAudioOutput::write(const void* buffer, size_t length) throw (CAudioError) {
323     if (__IsInit() == false || __IsReady() == false) {
324         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioOutput");
325     }
326
327     if (buffer == NULL) {
328         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameters are invalid - buffer:%p, length:%zu", buffer, length);
329     }
330
331     /* When write() is called in PulseAudio callback, bypass a pcm data to PulseAudioClient (For Asynchronous) */
332     if (mpPulseAudioClient->isInThread() == true) {
333         int ret = mpPulseAudioClient->write(buffer, length);
334         if (ret < 0) {
335             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "The written result is invalid ret:%d", ret);
336         }
337         return length;
338     }
339
340     try {
341         /* For synchronization */
342         internalLock();
343
344         // Sets synchronous flag
345         __mIsUsedSyncWrite = true;
346
347         size_t lengthIter = length;
348
349         while (lengthIter > 0) {
350             size_t l;
351
352             while ((l = mpPulseAudioClient->getWritableSize()) == 0) {
353 #ifdef _AUDIO_IO_DEBUG_TIMING_
354                 AUDIO_IO_LOGD("writableSize is [%d].. wait", l);
355 #endif
356                 internalWait();
357             }
358
359             if (l > lengthIter) {
360                 l = lengthIter;
361             }
362
363 #ifdef _AUDIO_IO_DEBUG_TIMING_
364             AUDIO_IO_LOGD("PulseAudioClient->write(buffer:%p, length:%d)", buffer, l);
365 #endif
366
367             int ret = mpPulseAudioClient->write(buffer, l);
368             if (ret < 0) {
369                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "The written result is invalid ret:%d", ret);
370             }
371
372             buffer = static_cast<const uint8_t*>(buffer) + l;
373             lengthIter -= l;
374         }  // End of while (length > 0)
375
376         // Unsets synchronous flag
377         __mIsUsedSyncWrite = false;
378         internalUnlock();
379     } catch (CAudioError e) {
380         // Unsets synchronous flag
381         __mIsUsedSyncWrite = false;
382         internalUnlock();
383         throw e;
384     }
385
386     return length;
387 }