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