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