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