e137075024358420f5c1dead7c186c94836ebbe6
[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 "CAudioIODef.h"
19
20
21 using namespace std;
22 using namespace tizen_media_audio;
23
24
25 /**
26  * class CAudioInput inherited by CAudioIO
27  */
28 CAudioInput::CAudioInput(CAudioInfo& info) :
29     CAudioIO(info),
30     __mpSyncReadDataPtr(NULL),
31     __mSyncReadIndex(0),
32     __mSyncReadLength(0),
33     __mIsUsedSyncRead(true) {
34 }
35
36 CAudioInput::CAudioInput(
37         unsigned int            sampleRate,
38         CAudioInfo::EChannel    channel,
39         CAudioInfo::ESampleType type,
40         CAudioInfo::EAudioType  audioType) :
41     __mpSyncReadDataPtr(NULL),
42     __mSyncReadIndex(0),
43     __mSyncReadLength(0),
44     __mIsUsedSyncRead(true) {
45     mAudioInfo = CAudioInfo(sampleRate, channel, type, audioType, -1);
46 }
47
48 CAudioInput::~CAudioInput() {
49 }
50
51 void CAudioInput::onStream(CPulseAudioClient* pClient, size_t length) {
52     assert(pClient);
53
54     /*
55      * Does not call CAudioIO::onStream() for synchronization
56      * if a user is using read()
57      */
58     if (__mIsUsedSyncRead == true) {
59 #ifdef _AUDIO_IO_DEBUG_TIMING_
60         AUDIO_IO_LOGD("Sync Read Mode! - signal! - pClient:[%p], length:[%d]", pClient, length);
61 #endif
62         internalSignal();
63         return;
64     }
65
66     /*
67      * Accrues callback function
68      */
69 #ifdef _AUDIO_IO_DEBUG_TIMING_
70     AUDIO_IO_LOGD("pClient:[%p], length:[%d]", pClient, length);
71 #endif
72     CAudioIO::onStream(pClient, length);
73 }
74
75 void CAudioInput::onInterrupt(CAudioSessionHandler* pHandler, int id, mm_sound_focus_type_e focus_type, mm_sound_focus_state_e state, const char *reason_for_change, const char *additional_info) {
76     assert(pHandler);
77     AUDIO_IO_LOGD("[pHandler:0x%x], [focus_type:%d], [state:%d], [reason_for_change:%s], [additional_info:%s]", pHandler, focus_type, state, reason_for_change, additional_info);
78     CAudioIO::onInterrupt(pHandler, id, focus_type, state, reason_for_change, additional_info);
79 }
80
81 void CAudioInput::onSignal(CAudioSessionHandler* pHandler, mm_sound_signal_name_t signal, int value) {
82     assert(pHandler);
83     AUDIO_IO_LOGD("[pHandler:0x%x], [signal:%d], [value:%d]", pHandler, signal, value);
84     CAudioIO::onSignal(pHandler, signal, value);
85 }
86
87 void CAudioInput::__setInit(bool flag) {
88     __mIsInit = flag;
89 }
90
91 bool CAudioInput::__IsInit() {
92     return (CAudioIO::isInit() == true && __mIsInit == true);
93 }
94
95 bool CAudioInput::__IsReady() {
96     return CAudioIO::IsReady();
97 }
98
99 void CAudioInput::initialize() throw (CAudioError) {
100     if (__IsInit() == true) {
101         return;
102     }
103
104     try {
105         CAudioIO::initialize();
106
107         // Create ASM Handler
108         mpAudioSessionHandler = new CAudioSessionHandler(CAudioSessionHandler::EAudioSessionType::AUDIO_SESSION_TYPE_CAPTURE, mAudioInfo, this);
109         if (mpAudioSessionHandler == NULL) {
110             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed to allocate CAudioSessionHandler object");
111         }
112
113         // Initialize ASM Handler
114         mpAudioSessionHandler->initialize();
115
116         __setInit(true);
117         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
118     } catch (CAudioError err) {
119         finalize();
120         throw err;
121     }
122 }
123
124 void CAudioInput::finalize() {
125     if (__IsInit() == false) {
126         AUDIO_IO_LOGD("Did not initialize");
127         return;
128     }
129
130     SAFE_FINALIZE(mpAudioSessionHandler);
131     SAFE_DELETE(mpAudioSessionHandler);
132
133     CAudioIO::finalize();
134
135     __setInit(false);
136 }
137
138 void CAudioInput::prepare() throw (CAudioError) {
139     if (__IsInit() == false) {
140         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput");
141     }
142
143     if (__IsReady() == true) {
144         AUDIO_IO_LOGD("Already prepared CAudioInput");
145         return;
146     }
147
148     try {
149         internalLock();
150
151         // Check to invalid AudioType
152         CAudioInfo::EAudioType audioType = mAudioInfo.getAudioType();
153         if (audioType < CAudioInfo::EAudioType::AUDIO_IN_TYPE_MEDIA || audioType > CAudioInfo::EAudioType::AUDIO_IN_TYPE_LOOPBACK) {
154             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "The audioType is invalid [type:%d]", static_cast<int>(audioType));
155         }
156
157         if (mpAudioSessionHandler->getId() < 0) {  //Did not registerSound()
158             // Check session to skip registration
159             if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
160                 // Register ASM Listener
161                 AUDIO_IO_LOGD("Register ASM Listener");
162                 mpAudioSessionHandler->registerSound();
163             }
164         }
165
166         // Init StreamSpec
167         AUDIO_IO_LOGD("Set Stream Spec : CPulseStreamSpec::STREAM_LATENCY_INPUT_MID");
168         CPulseStreamSpec::EStreamLatency streamSpec = CPulseStreamSpec::EStreamLatency::STREAM_LATENCY_INPUT_MID;
169         CPulseStreamSpec spec(streamSpec, mAudioInfo);
170
171         // Create PulseAudio Handler
172         mpPulseAudioClient = new CPulseAudioClient(CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_RECORD, spec, this);
173         if (mpPulseAudioClient == NULL) {
174             THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed to allocate CPulseAudioClient object");
175         }
176
177         // Initialize PulseAudio Handler
178         mpPulseAudioClient->initialize();
179
180         if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
181             /* Updates ASM to PLAYING */
182             mpAudioSessionHandler->updatePlaying();
183         }
184
185         internalUnlock();
186
187         // Do Prepare
188         CAudioIO::prepare();
189     } catch (CAudioError e) {
190         internalUnlock();
191         throw e;
192     }
193 }
194
195 void CAudioInput::unprepare() throw (CAudioError) {
196     if (__IsInit() == false) {
197         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput");
198     }
199
200     if (__IsReady() == false) {
201         AUDIO_IO_LOGD("Already unprepared");
202         return;
203     }
204
205     try{
206         // Do unprepare
207         CAudioIO::unprepare();
208
209         internalLock();
210
211         SAFE_FINALIZE(mpPulseAudioClient);
212         SAFE_DELETE(mpPulseAudioClient);
213
214         if (mpAudioSessionHandler->getId() >= 0) {
215             /* Updates ASM to STOP */
216             if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
217                 mpAudioSessionHandler->updateStop();
218             }
219
220             bool isSkip = mpAudioSessionHandler->isSkipSessionEvent();
221             if (isSkip == false) {
222                 mpAudioSessionHandler->unregisterSound();
223             }
224         }
225
226         internalUnlock();
227
228         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE);
229     } catch (CAudioError e) {
230         internalUnlock();
231         throw e;
232     }
233 }
234
235 void CAudioInput::pause() throw (CAudioError) {
236     if (__IsInit() == false || __IsReady() == false) {
237         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
238     }
239
240     try{
241         CAudioIO::pause();
242
243         internalLock();
244
245         /* Updates ASM to STOP */
246         if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
247             mpAudioSessionHandler->updateStop();
248         }
249
250         internalUnlock();
251
252         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
253     } catch (CAudioError e) {
254         internalUnlock();
255         throw e;
256     }
257 }
258
259 void CAudioInput::resume() throw (CAudioError) {
260     if (__IsInit() == false || __IsReady() == false) {
261         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
262     }
263
264     try {
265         internalLock();
266
267         if (isForceIgnore() == false && mpAudioSessionHandler->isSkipSessionEvent() == false) {
268
269             /* Updates ASM to PLAYING */
270             mpAudioSessionHandler->updatePlaying();
271         }
272
273         internalUnlock();
274
275         CAudioIO::resume();
276
277         CAudioIO::onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
278     } catch (CAudioError e) {
279         internalUnlock();
280         throw e;
281     }
282 }
283
284 void CAudioInput::drain() throw (CAudioError) {
285     THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "Did not support drain of CAudioInput");
286 }
287
288 void CAudioInput::flush() throw (CAudioError) {
289     if (__IsInit() == false || __IsReady() == false) {
290         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
291     }
292
293     try {
294         CAudioIO::flush();
295     } catch (CAudioError e) {
296         throw e;
297     }
298 }
299
300 int CAudioInput::getBufferSize() throw (CAudioError) {
301     if (__IsInit() == false) {
302         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput");
303     }
304
305     if (__IsReady() == false) {
306         AUDIO_IO_LOGD("Warning: Did not prepare CAudioInput, then return zero");
307         return 0;
308     }
309
310     int size = 0;
311
312     try {
313         size = mpPulseAudioClient->getBufferSize();
314     } catch (CAudioError err) {
315         throw err;
316     }
317
318     return size;
319 }
320
321 void CAudioInput::setStreamCallback(SStreamCallback callback) throw (CAudioError) {
322     if (__IsInit() == false) {
323         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput");
324     }
325
326     if (callback.onStream == NULL) {
327         AUDIO_IO_LOGD("__mIsUsedSyncRead = true");
328         __mIsUsedSyncRead = true;
329     } else {
330         AUDIO_IO_LOGD("__mIsUsedSyncRead = false");
331         __mIsUsedSyncRead = false;
332     }
333
334     CAudioIO::setStreamCallback(callback);
335 }
336
337 size_t CAudioInput::read(void* buffer, size_t length) throw (CAudioError) {
338     if (__IsInit() == false || __IsReady() == false) {
339         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
340     }
341
342     if (buffer == NULL) {
343         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameters are invalid - buffer:%p, length:%zu", buffer, length);
344     }
345
346     /* Checks synchronous flag */
347     if (__mIsUsedSyncRead == false) {
348         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "Invalid operation of read() if receive stream callback");
349     }
350
351     size_t lengthIter = length;
352     int ret = 0;
353
354     try {
355         internalLock();
356
357         while (lengthIter > 0) {
358             size_t l;
359
360             while (__mpSyncReadDataPtr == NULL) {
361                 ret = mpPulseAudioClient->peek(&__mpSyncReadDataPtr, &__mSyncReadLength);
362                 if (ret != 0) {
363                     THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed CPulseAudioClient::peek() ret:[%d]", ret);
364                 }
365
366                 if (__mSyncReadLength <= 0) {
367 #ifdef _AUDIO_IO_DEBUG_TIMING_
368                     AUDIO_IO_LOGD("readLength(%d byte) is not valid.. wait..", __mSyncReadLength);
369 #endif
370                     internalWait();
371                 } else if (__mpSyncReadDataPtr == NULL) {
372                     /* There's a hole in the stream, skip it. We could generate
373                      * silence, but that wouldn't work for compressed streams.
374                      */
375                     ret = mpPulseAudioClient->drop();
376                     if (ret != 0) {
377                         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed CPulseAudioClient::drop() ret:[%d]", ret);
378                     }
379                 } else {
380                     __mSyncReadIndex = 0;
381                 }
382             }//end of while (pReadData == NULL)
383
384             if (__mSyncReadLength < lengthIter) {
385                 l = __mSyncReadLength;
386             } else {
387                 l = lengthIter;
388             }
389
390             // Copy partial pcm data on out parameter
391 #ifdef _AUDIO_IO_DEBUG_TIMING_
392             AUDIO_IO_LOGD("memcpy() that a peeked buffer(%p), index(%d), length(%d), on out buffer", (const uint8_t*)(__mpSyncReadDataPtr) + __mSyncReadIndex, __mSyncReadIndex, l);
393 #endif
394             memcpy(buffer, (const uint8_t*)__mpSyncReadDataPtr + __mSyncReadIndex, l);
395
396             // Move next position
397             buffer = (uint8_t*)buffer + l;
398             lengthIter -= l;
399
400             // Adjusts the rest length
401             __mSyncReadIndex  += l;
402             __mSyncReadLength -= l;
403
404             if (__mSyncReadLength == 0) {
405 #ifdef _AUDIO_IO_DEBUG_TIMING_
406                 AUDIO_IO_LOGD("__mSyncReadLength is zero - Do drop()");
407 #endif
408                 ret = mpPulseAudioClient->drop();
409                 if (ret != 0) {
410                     THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed CPulseAudioClient::drop() ret:[%d]", ret);
411                 }
412
413                 // Reset the internal pointer
414                 __mpSyncReadDataPtr = NULL;
415                 __mSyncReadLength   = 0;
416                 __mSyncReadIndex    = 0;
417             }
418         }  // End of while (length > 0)
419
420         internalUnlock();
421     } catch (CAudioError e) {
422         internalUnlock();
423         throw e;
424     }
425
426     return length;
427 }
428
429 int CAudioInput::peek(const void** buffer, size_t* length) throw (CAudioError) {
430     if (__IsInit() == false || __IsReady() == false) {
431         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
432     }
433
434     if (buffer == NULL || length == NULL) {
435         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameters are NULL buffer:%p, length:%p", buffer, length);
436     }
437
438     /* Checks synchronous flag */
439     if (__mIsUsedSyncRead == true) {
440         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "Invalid operation of peek() if does not receive a stream callback");
441     }
442
443     int ret = 0;
444
445     try {
446         ret = mpPulseAudioClient->peek(buffer, length);
447     } catch (CAudioError e) {
448         throw e;
449     }
450
451     return ret;
452 }
453
454 int CAudioInput::drop() throw (CAudioError) {
455     if (__IsInit() == false || __IsReady() == false) {
456         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
457     }
458
459     /* Checks synchronous flag */
460     if (__mIsUsedSyncRead == true) {
461         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "Invalid operation of drop() if does not receive a stream callback");
462     }
463
464     int ret = 0;
465
466     try {
467         ret = mpPulseAudioClient->drop();
468     } catch (CAudioError e) {
469         throw e;
470     }
471
472     return ret;
473 }