ee5f3b6d40e4d0dbf4560e9718c3bd25b9fbe24c
[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(1.0) {
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::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
94 }
95
96 void CAudioInput::finalize() {
97     if (!__IsInit()) {
98 //LCOV_EXCL_START
99         AUDIO_IO_LOGD("Did not initialize");
100         return;
101 //LCOV_EXCL_STOP
102     }
103
104     CAudioIO::finalize();
105     __setInit(false);
106     __mVolume = 1.0;
107 }
108
109 void CAudioInput::prepare() {
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         std::unique_lock<std::mutex> mutex(mMutex);
137         mpPulseAudioClient = new CPulseAudioClient(CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_RECORD, spec, this);
138         mpPulseAudioClient->initialize();
139         mpPulseAudioClient->applyRecordVolume(__mVolume);
140 #ifndef DISABLE_MOBILE_BACK_COMP
141         /* Uncork stream which is created with CORKED flag */
142         mpPulseAudioClient->cork(false);
143 #endif
144         mutex.unlock();
145
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     if (!__IsInit())
160         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, //LCOV_EXCL_LINE
161                         "Did not initialize CAudioInput");          //LCOV_EXCL_LINE
162
163     if (!__IsReady()) {
164         AUDIO_IO_LOGD("Already unprepared");
165         return;
166     }
167
168     CAudioIO::unprepare();
169
170     std::unique_lock<std::mutex> mutex(mMutex);
171     if (mpPulseAudioClient && mpPulseAudioClient->isInThread())
172         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't unprepare inside pulseaudio thread");
173     SAFE_FINALIZE(mpPulseAudioClient);
174     SAFE_DELETE(mpPulseAudioClient);
175     mutex.unlock();
176
177     CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
178 }
179
180 void CAudioInput::pause() {
181     if (!__IsInit() || !__IsReady())
182         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,   //LCOV_EXCL_LINE
183                         "Did not initialize or prepare CAudioInput"); //LCOV_EXCL_LINE
184
185     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING)
186         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE,
187                         "Can't pause if not in Running state");
188
189     if (mpPulseAudioClient->isInThread())
190         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't pause in thread"); //LCOV_EXCL_LINE
191
192     CAudioIO::pause();
193     CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
194 }
195
196 void CAudioInput::resume() {
197     if (!__IsInit() || !__IsReady())
198         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,   //LCOV_EXCL_LINE
199                         "Did not initialize or prepare CAudioInput"); //LCOV_EXCL_LINE
200
201     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED)
202         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE,
203                         "Can't resume if not in Paused state");
204
205     if (mpPulseAudioClient->isInThread())
206         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_OPERATION, "Can't resume in thread"); //LCOV_EXCL_LINE
207
208     CAudioIO::resume();
209     CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
210 }
211
212 void CAudioInput::flush() {
213     if (!__IsInit() || !__IsReady())
214         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,   //LCOV_EXCL_LINE
215                         "Did not initialize or prepare CAudioInput"); //LCOV_EXCL_LINE
216
217     CAudioIO::flush();
218 }
219
220 int CAudioInput::getBufferSize() {
221     if (!__IsInit())
222         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput"); //LCOV_EXCL_LINE
223
224     /* FIXME : return calculated size here to satisfy backward compatibility */
225     return (mAudioInfo.getSampleRate() * DEFAULT_PERIOD_SIZE) / 1000 * mAudioInfo.getSampleSize();
226 }
227
228 void CAudioInput::setStreamCallback(SStreamCallback callback) {
229     if (!__IsInit())
230         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput"); //LCOV_EXCL_LINE
231
232     __mIsUsedSyncRead = (callback.onStream == nullptr);
233
234     AUDIO_IO_LOGD("__mIsUsedSyncRead = %d", __mIsUsedSyncRead);
235
236     CAudioIO::setStreamCallback(callback);
237 }
238
239 size_t CAudioInput::read(void* buffer, size_t length) {
240     if (!__IsInit() || !__IsReady())
241         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,   //LCOV_EXCL_LINE
242                         "Did not initialize or prepare CAudioInput"); //LCOV_EXCL_LINE
243
244     if (!buffer)
245         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT,
246                                "Parameters are NULL buffer:%p", buffer);
247
248     if (CAudioIO::getState() != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING)
249         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
250                         "Can't read if not in Running state");
251
252     /* Checks synchronous flag */
253     if (!__mIsUsedSyncRead)
254         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
255                         "Invalid operation of read() if receive stream callback");
256
257     int ret = 0;
258
259     std::unique_lock<std::mutex> mutex(mMutex);
260     // If another thread did call unprepare, do not read
261     if (!mpPulseAudioClient)
262         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, //LCOV_EXCL_LINE
263                         "Did not initialize CPulseAudioClient");    //LCOV_EXCL_LINE
264
265     // Block until read done
266     ret = mpPulseAudioClient->read(buffer, length);
267     mutex.unlock();
268
269     sched_yield();
270
271     return ret;
272 }
273
274 int CAudioInput::peek(const void** buffer, size_t* length) {
275     if (!__IsInit() || !__IsReady())
276         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED,   //LCOV_EXCL_LINE
277                         "Did not initialize or prepare CAudioInput"); //LCOV_EXCL_LINE
278
279     if (buffer == nullptr || length == nullptr)
280         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT,                 //LCOV_EXCL_LINE
281                                "Parameters are NULL buffer:%p, length:%p", buffer, length); //LCOV_EXCL_LINE
282
283     /* Checks synchronous flag */
284     if (__mIsUsedSyncRead)
285         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
286                         "Invalid operation of peek() if does not receive a stream callback");
287
288     return mpPulseAudioClient->peek(buffer, length);
289 }
290
291 int CAudioInput::drop() {
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     /* Checks synchronous flag */
297     if (__mIsUsedSyncRead)
298         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION,
299                         "Invalid operation of drop() if does not receive a stream callback");
300
301     return mpPulseAudioClient->drop();
302 }
303
304 void CAudioInput::setVolume(double volume) {
305     if (!__IsInit())
306         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Not initialized"); //LCOV_EXCL_LINE
307
308     if (__IsReady()) {
309         std::unique_lock<std::mutex> defer_mutex(mMutex, std::defer_lock);
310         if (!mpPulseAudioClient->isInThread())
311             defer_mutex.lock();
312
313         mpPulseAudioClient->applyRecordVolume(volume);
314     }
315
316     __mVolume = volume;
317 }
318
319 double CAudioInput::getVolume() {
320     if (!__IsInit())
321         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Not initialized"); //LCOV_EXCL_LINE
322
323     return __mVolume;
324 }