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