Merge branch 'tizen_3.0' into tizen
[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_DEFAULT");
163         CPulseStreamSpec::EStreamLatency streamSpec = CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_OUTPUT_DEFAULT;
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     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING) {
234         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE,
235                         "Can't pause if not in Running state");
236     }
237
238     if (mpPulseAudioClient->isInThread() == true) {
239         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't pause in thread");
240     }
241
242     try {
243         CAudioIO::pause();
244
245         internalLock();
246
247         /* Updates ASM to STOP */
248         if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
249             mpAudioSessionHandler->updateStop();
250         }
251
252         internalUnlock();
253
254         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
255     } catch (CAudioError e) {
256         internalUnlock();
257         throw e;
258     }
259 }
260
261 void CAudioOutput::resume() throw(CAudioError) {
262     if (__IsInit() == false || __IsReady() == false) {
263         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioOutput");
264     }
265
266     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED) {
267         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE,
268                         "Can't resume if not in Paused state");
269     }
270
271     if (mpPulseAudioClient->isInThread() == true) {
272         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't resume in thread");
273     }
274
275     try {
276         internalLock();
277
278         if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
279             /* Updates ASM to PLAYING */
280             mpAudioSessionHandler->updatePlaying();
281         }
282
283         internalUnlock();
284
285         CAudioIO::resume();
286
287         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
288     } catch (CAudioError e) {
289         internalUnlock();
290         throw e;
291     }
292 }
293
294 void CAudioOutput::drain() throw(CAudioError) {
295     if (__IsInit() == false || __IsReady() == false) {
296         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioOutput");
297     }
298
299     try {
300         CAudioIO::drain();
301     } catch (CAudioError e) {
302         throw e;
303     }
304 }
305
306 void CAudioOutput::flush() throw(CAudioError) {
307     if (__IsInit() == false || __IsReady() == false) {
308         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioOutput");
309     }
310
311     try {
312         CAudioIO::flush();
313     } catch (CAudioError e) {
314         throw e;
315     }
316 }
317
318 int CAudioOutput::getBufferSize() throw(CAudioError) {
319     if (__IsInit() == false) {
320         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioOutput");
321     }
322
323     /* FIXME : return calculated size here to satisfy backward compatibility */
324     return (mAudioInfo.getSampleRate() * DEFAULT_PERIOD_SIZE) / 1000 * mAudioInfo.getSampleSize();
325 }
326
327 size_t CAudioOutput::write(const void* buffer, size_t length) throw(CAudioError) {
328     if (__IsInit() == false || __IsReady() == false) {
329         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioOutput");
330     }
331
332     if (buffer == NULL) {
333         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameters are invalid - buffer:%p, length:%zu", buffer, length);
334     }
335     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING) {
336         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
337                         "Can't write if not in Running state");
338     }
339
340     /* When write() is called in PulseAudio callback, bypass a pcm data to CPulseAudioClient (For Asynchronous) */
341     if (mpPulseAudioClient->isInThread() == true) {
342         int ret = mpPulseAudioClient->write(buffer, length);
343         if (ret < 0) {
344             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "The written result is invalid ret:%d", ret);
345         }
346         return length;
347     }
348
349     try {
350         /* For synchronization */
351         internalLock();
352
353         // If another thread did call unprepare, do not write
354         if (mpPulseAudioClient == NULL)
355             THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CPulseAudioClient");
356
357         // Sets synchronous flag
358         __mIsUsedSyncWrite = true;
359
360         size_t lengthIter = length;
361
362         while (lengthIter > 0) {
363             size_t l;
364
365             while ((l = mpPulseAudioClient->getWritableSize()) == 0) {
366 #ifdef _AUDIO_IO_DEBUG_TIMING_
367                 AUDIO_IO_LOGD("writableSize is [%d].. wait", l);
368 #endif
369                 internalWait();
370             }
371
372             if (l > lengthIter) {
373                 l = lengthIter;
374             }
375
376 #ifdef _AUDIO_IO_DEBUG_TIMING_
377             AUDIO_IO_LOGD("CPulseAudioClient->write(buffer:%p, length:%d)", buffer, l);
378 #endif
379
380             int ret = mpPulseAudioClient->write(buffer, l);
381             if (ret < 0) {
382                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "The written result is invalid ret:%d", ret);
383             }
384
385             buffer = static_cast<const uint8_t*>(buffer) + l;
386             lengthIter -= l;
387         }  // End of while (length > 0)
388
389         // Unsets synchronous flag
390         __mIsUsedSyncWrite = false;
391         internalUnlock();
392     } catch (CAudioError e) {
393         // Unsets synchronous flag
394         __mIsUsedSyncWrite = false;
395         internalUnlock();
396         throw e;
397     }
398
399     return length;
400 }