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