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