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