Use in-class initializer for class members
[platform/core/api/audio-io.git] / src / cpp / CAudioInput.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 <new>
19
20 #include "CAudioIODef.h"
21 #include <sched.h>
22 #include "cpp_audio_in_privilege.h"
23
24 #define RECORDER_PRIVILEGE "http://tizen.org/privilege/recorder"
25 #define CLIENT_NAME "AUDIO_IO_PA_CLIENT"
26
27 using namespace std;
28 using namespace tizen_media_audio;
29
30 /**
31  * class CAudioInput inherited by CAudioIO
32  */
33 CAudioInput::CAudioInput(CAudioInfo& info) :
34     CAudioIO(info) {
35     mDirection = CAudioInfo::EAudioDirection::AUDIO_DIRECTION_IN;
36 }
37
38 void CAudioInput::onStream(CPulseAudioClient* pClient, size_t length) {
39     assert(pClient);
40
41     /*
42      * Does not call CAudioIO::onStream() for synchronization
43      * if a user is using read()
44      */
45     if (__mIsUsedSyncRead) {
46 #ifdef _AUDIO_IO_DEBUG_TIMING_
47         AUDIO_IO_LOGD("Sync Read Mode! - pClient:[%p], length:[%zu]", pClient, length);
48 #endif
49         return;
50     }
51
52     /*
53      * Accrues callback function
54      */
55 #ifdef _AUDIO_IO_DEBUG_TIMING_
56     AUDIO_IO_LOGD("pClient:[%p], length:[%zu]", pClient, length);
57 #endif
58     CAudioIO::onStream(pClient, length);
59 }
60
61 void CAudioInput::__setInit(bool flag) noexcept {
62     __mIsInit = flag;
63 }
64
65 bool CAudioInput::__IsInit() noexcept {
66     return (CAudioIO::isInit() && __mIsInit);
67 }
68
69 bool CAudioInput::__IsReady() noexcept {
70     return CAudioIO::IsReady();
71 }
72
73 void CAudioInput::initialize() {
74     if (__IsInit())
75         return;
76
77     if (!cpp_audio_in_has_record_privilege())
78         THROW_ERROR_MSG(CAudioError::EError::ERROR_PERMISSION_DENIED, "No privilege for record");
79
80     try {
81         CAudioIO::initialize();
82         __setInit(true);
83     } catch (const CAudioError& e) {
84 //LCOV_EXCL_START
85         finalize();
86         throw;
87 //LCOV_EXCL_STOP
88     }
89
90     CAudioIO::setState(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
91     CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
92 }
93
94 void CAudioInput::finalize() {
95     if (!__IsInit()) {
96 //LCOV_EXCL_START
97         AUDIO_IO_LOGD("Did not initialize");
98         return;
99 //LCOV_EXCL_STOP
100     }
101
102     CAudioIO::finalize();
103     __setInit(false);
104     __mVolume = CAudioInfo::DEFAULT_RECORD_VOLUME;
105 }
106
107 void CAudioInput::prepare() {
108     CAudioTimedLocker locker(__func__, mMutex);
109
110     if (!__IsInit())
111         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput"); //LCOV_EXCL_LINE
112
113     if (__IsReady()) {
114         AUDIO_IO_LOGD("Already prepared CAudioInput");
115         CAudioIO::prepare();
116         return;
117     }
118
119     /* Check invalid AudioType */
120     CAudioInfo::EAudioType audioType = mAudioInfo.getAudioType();
121     if (audioType < CAudioInfo::EAudioType::AUDIO_IN_TYPE_MEDIA ||
122         audioType >= CAudioInfo::EAudioType::AUDIO_OUT_TYPE_MEDIA)
123         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT,
124                                "The audioType is invalid [type:%d]", static_cast<int>(audioType));
125
126     try {
127         /* Init StreamSpec */
128         AUDIO_IO_LOGD("Set Stream Spec : CPulseStreamSpec::STREAM_LATENCY_INPUT_DEFAULT");
129         CPulseStreamSpec::EStreamLatency streamSpec = CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_INPUT_DEFAULT;
130         /* Override the default value by audio type */
131         if (audioType == CAudioInfo::EAudioType::AUDIO_IN_TYPE_VOIP)
132             streamSpec = CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_INPUT_VOIP;
133
134         CPulseStreamSpec spec(streamSpec, mAudioInfo);
135
136         mpPulseAudioClient = new CPulseAudioClient(CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_RECORD, spec, this);
137         mpPulseAudioClient->initialize();
138         mpPulseAudioClient->applyRecordVolume(__mVolume);
139 #ifndef DISABLE_MOBILE_BACK_COMP
140         /* Uncork stream which is created with CORKED flag */
141         mpPulseAudioClient->cork(false);
142 #endif
143         CAudioIO::prepare();
144     } catch (const CAudioError& e) {
145 //LCOV_EXCL_START
146         SAFE_FINALIZE(mpPulseAudioClient);
147         SAFE_DELETE(mpPulseAudioClient);
148         throw;
149 //LCOV_EXCL_STOP
150     } catch (const std::bad_alloc&) {
151         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed to allocate CPulseAudioClient object");
152     }
153 }
154
155 void CAudioInput::unprepare() {
156     CAudioTimedLocker locker(__func__, mMutex);
157
158     if (!__IsInit())
159         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, //LCOV_EXCL_LINE
160                         "Did not initialize CAudioInput");          //LCOV_EXCL_LINE
161
162     if (!__IsReady()) {
163         AUDIO_IO_LOGD("Already unprepared");
164         return;
165     }
166
167     CAudioIO::unprepare();
168
169     if (mpPulseAudioClient && mpPulseAudioClient->isInThread())
170         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't unprepare inside pulseaudio thread");
171
172     SAFE_FINALIZE(mpPulseAudioClient);
173     SAFE_DELETE(mpPulseAudioClient);
174
175     CAudioIO::setState(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
176
177     locker.unlock();
178
179     CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
180 }
181
182 void CAudioInput::pause() {
183    CAudioTimedLocker locker(__func__, mMutex);
184
185     if (!__IsInit() || !__IsReady())
186         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,   //LCOV_EXCL_LINE
187                         "Did not initialize or prepare CAudioInput"); //LCOV_EXCL_LINE
188
189     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING)
190         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE,
191                         "Can't pause if not in Running state");
192
193     if (mpPulseAudioClient->isInThread())
194         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't pause in thread"); //LCOV_EXCL_LINE
195
196     CAudioIO::pause();
197     CAudioIO::setState(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
198
199     locker.unlock();
200
201     CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
202 }
203
204 void CAudioInput::resume() {
205     CAudioTimedLocker locker(__func__, mMutex);
206
207     if (!__IsInit() || !__IsReady())
208         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,   //LCOV_EXCL_LINE
209                         "Did not initialize or prepare CAudioInput"); //LCOV_EXCL_LINE
210
211     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED)
212         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE,
213                         "Can't resume if not in Paused state");
214
215     if (mpPulseAudioClient->isInThread())
216         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't resume in thread"); //LCOV_EXCL_LINE
217
218     CAudioIO::resume();
219     CAudioIO::setState(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
220
221     locker.unlock();
222
223     CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
224 }
225
226 void CAudioInput::flush() {
227     CAudioTimedLocker locker(__func__, mMutex);
228
229     if (!__IsInit() || !__IsReady())
230         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,   //LCOV_EXCL_LINE
231                         "Did not initialize or prepare CAudioInput"); //LCOV_EXCL_LINE
232
233     CAudioIO::flush();
234 }
235
236 int CAudioInput::getBufferSize() {
237     if (!__IsInit())
238         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput"); //LCOV_EXCL_LINE
239
240     /* FIXME : return calculated size here to satisfy backward compatibility */
241     return (mAudioInfo.getSampleRate() * DEFAULT_PERIOD_SIZE) / 1000 * mAudioInfo.getSampleSize();
242 }
243
244 void CAudioInput::setStreamCallback(SStreamCallback callback) {
245     if (!__IsInit())
246         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput"); //LCOV_EXCL_LINE
247
248     __mIsUsedSyncRead = (callback.onStream == nullptr);
249
250     AUDIO_IO_LOGD("__mIsUsedSyncRead = %d", __mIsUsedSyncRead);
251
252     CAudioIO::setStreamCallback(callback);
253 }
254
255 size_t CAudioInput::read(void* buffer, size_t length) {
256     CAudioTimedLocker locker(__func__, mMutex);
257
258     if (!__IsInit() || !__IsReady())
259         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,   //LCOV_EXCL_LINE
260                         "Did not initialize or prepare CAudioInput"); //LCOV_EXCL_LINE
261
262     if (!buffer)
263         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT,
264                                "Parameters are NULL buffer:%p", buffer);
265
266     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING)
267         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
268                         "Can't read if not in Running state");
269
270     /* Checks synchronous flag */
271     if (!__mIsUsedSyncRead)
272         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
273                         "Invalid operation of read() if receive stream callback");
274
275     int ret = 0;
276
277     // If another thread did call unprepare, do not read
278     if (!mpPulseAudioClient)
279         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, //LCOV_EXCL_LINE
280                         "Did not initialize CPulseAudioClient");    //LCOV_EXCL_LINE
281
282     // Block until read done
283     ret = mpPulseAudioClient->read(buffer, length);
284     locker.unlock();
285
286     sched_yield();
287
288     return ret;
289 }
290
291 int CAudioInput::peek(const void** buffer, size_t* length) {
292     if (!__IsInit() || !__IsReady())
293         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,   //LCOV_EXCL_LINE
294                         "Did not initialize or prepare CAudioInput"); //LCOV_EXCL_LINE
295
296     if (buffer == nullptr || length == nullptr)
297         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT,                 //LCOV_EXCL_LINE
298                                "Parameters are NULL buffer:%p, length:%p", buffer, length); //LCOV_EXCL_LINE
299
300     /* Checks synchronous flag */
301     if (__mIsUsedSyncRead)
302         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
303                         "Invalid operation of peek() if does not receive a stream callback");
304
305     return mpPulseAudioClient->peek(buffer, length);
306 }
307
308 int CAudioInput::drop() {
309     if (!__IsInit() || !__IsReady())
310         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,   //LCOV_EXCL_LINE
311                         "Did not initialize or prepare CAudioInput"); //LCOV_EXCL_LINE
312
313     /* Checks synchronous flag */
314     if (__mIsUsedSyncRead)
315         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
316                         "Invalid operation of drop() if does not receive a stream callback");
317
318     return mpPulseAudioClient->drop();
319 }
320
321 void CAudioInput::setVolume(double volume) {
322     if (!__IsInit())
323         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Not initialized"); //LCOV_EXCL_LINE
324
325     if (__IsReady()) {
326         CAudioTimedLocker locker(__func__);
327         if (!mpPulseAudioClient->isInThread())
328             locker.lock(mMutex, mutex_timeout_s);
329
330         mpPulseAudioClient->applyRecordVolume(volume);
331     }
332
333     __mVolume = volume;
334 }
335
336 double CAudioInput::getVolume() {
337     if (!__IsInit())
338         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Not initialized"); //LCOV_EXCL_LINE
339
340     return __mVolume;
341 }