f050401892bc294edc2c312a9da1f9e1f5eb9656
[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         CAudioIO::onStateChanged(CAudioInfo::AUDIO_IO_STATE_RUNNING);
266     } catch (CAudioError e) {
267         internalUnlock();
268         throw e;
269     }
270 }
271
272 void CAudioOutput::drain() throw (CAudioError) {
273     if (IsInit() == false || IsReady() == false) {
274         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioOutput");
275     }
276
277     try {
278         CAudioIO::drain();
279     } catch (CAudioError e) {
280         internalUnlock();
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         internalUnlock();
294         throw e;
295     }
296 }
297
298 int CAudioOutput::getBufferSize() throw (CAudioError) {
299     if (IsInit() == false) {
300         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioOutput");
301     }
302
303     if (IsReady() == false) {
304         AUDIO_IO_LOGD("Warning: Did not prepare CAudioOutput, then return zero");
305         return 0;
306     }
307
308     int size = 0;
309
310     try {
311         internalLock();
312         size = mpPulseAudioClient->getBufferSize();
313         internalUnlock();
314     } catch (CAudioError err) {
315         internalUnlock();
316         throw err;
317     }
318
319     return size;
320 }
321
322 int CAudioOutput::write(const void* buffer, unsigned int length) throw (CAudioError) {
323     if (IsInit() == false || IsReady() == false) {
324         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioOutput");
325     }
326
327     if (buffer == NULL) {
328         THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_INVALID_ARGUMENT, "Parameters are invalid - buffer:%p, length:%d", buffer, length);
329     }
330
331     /*
332      * Check skip condition.
333      * If accessibility screen reader (VOICE type with NoSession), no need to check, always do write.
334      */
335     if (mpAudioSessionHandler->isSkipSessionEvent() == false) {
336         /* Check whether voicerecorder is running */
337         int vrState = 0;
338
339         vconf_get_int(VCONFKEY_RECORDER_STATE, &vrState);
340         if (vrState == VCONFKEY_RECORDER_STATE_RECORDING) {
341             THROW_ERROR_MSG(CAudioError::ERROR_POLICY_BLOCKED, "During Voicerecording --> MUTE");
342         }
343     }
344
345     /* When write() is called in PulseAudio callback, bypass a pcm data to PulseAudioClient (For  Asynchronous) */
346     if (mpPulseAudioClient->isInThread() == true) {
347         int ret = mpPulseAudioClient->write(buffer, length);
348         if (ret == 0) {
349             return length;
350         }
351     }
352
353     /* For synchronization */
354     internalLock();
355
356     // Sets synchronous flag
357     mIsUsedSyncWrite = true;
358
359     unsigned int lengthIter = length;
360     try {
361         while (lengthIter > 0) {
362             int l, r;
363
364             while ((l = mpPulseAudioClient->getWritableSize()) == 0) {
365 #ifdef _AUDIO_IO_DEBUG_TIMING_
366                 AUDIO_IO_LOGD("writableSize is [%d].. wait", l);
367 #endif
368                 internalWait();
369             }
370
371             if (l == -1) {
372                 THROW_ERROR_MSG(CAudioError::ERROR_INTERNAL_OPERATION, "The Writable size is invalid");
373             }
374
375             if (static_cast<unsigned int>(l) > lengthIter) {
376                 l = lengthIter;
377             }
378
379 #ifdef _AUDIO_IO_DEBUG_TIMING_
380             AUDIO_IO_LOGD("PulseAudioClient->write(buffer:%p, length:%d)", buffer, l);
381 #endif
382
383             r = mpPulseAudioClient->write(buffer, l);
384             if (r < 0) {
385                 THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_INTERNAL_OPERATION, "The written result is invalid ret:%d", r);
386             }
387
388             buffer = static_cast<const uint8_t*>(buffer) + l;
389             lengthIter -= l;
390         }//end of while (length > 0)
391     } catch (CAudioError e) {
392         // Unsets synchronous flag
393         mIsUsedSyncWrite = false;
394         internalUnlock();
395         throw e;
396     }
397
398     // Unsets synchronous flag
399     mIsUsedSyncWrite = false;
400     internalUnlock();
401
402     return length;
403 }