2f70ed67ede2aae9cf207ea28f1486d8607d3279
[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             /* 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             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "The written result is invalid ret:%d", ret);
335         }
336         return length;
337     }
338
339     try {
340         /* For synchronization */
341         internalLock();
342
343         // Sets synchronous flag
344         __mIsUsedSyncWrite = true;
345
346         size_t lengthIter = length;
347
348         while (lengthIter > 0) {
349             size_t l;
350
351             while ((l = mpPulseAudioClient->getWritableSize()) == 0) {
352 #ifdef _AUDIO_IO_DEBUG_TIMING_
353                 AUDIO_IO_LOGD("writableSize is [%d].. wait", l);
354 #endif
355                 internalWait();
356             }
357
358             if (l > lengthIter) {
359                 l = lengthIter;
360             }
361
362 #ifdef _AUDIO_IO_DEBUG_TIMING_
363             AUDIO_IO_LOGD("PulseAudioClient->write(buffer:%p, length:%d)", buffer, l);
364 #endif
365
366             int ret = mpPulseAudioClient->write(buffer, l);
367             if (ret < 0) {
368                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "The written result is invalid ret:%d", ret);
369             }
370
371             buffer = static_cast<const uint8_t*>(buffer) + l;
372             lengthIter -= l;
373         }  // End of while (length > 0)
374
375         // Unsets synchronous flag
376         __mIsUsedSyncWrite = false;
377         internalUnlock();
378     } catch (CAudioError e) {
379         // Unsets synchronous flag
380         __mIsUsedSyncWrite = false;
381         internalUnlock();
382         throw e;
383     }
384
385     return length;
386 }