9440bc2802eb2f754c20569a51b58400b6681be8
[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::AUDIO_SESSION_TYPE_CAPTURE, mAudioInfo, this);
109         if (mpAudioSessionHandler == NULL) {
110             THROW_ERROR_MSG(CAudioError::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::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::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::AUDIO_IN_TYPE_MEDIA || audioType > CAudioInfo::AUDIO_IN_TYPE_LOOPBACK) {
154             THROW_ERROR_MSG_FORMAT(CAudioError::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 Strem Spec : CPulseStreamSpec::STREAM_LATENCY_INPUT_MID");
168         CPulseStreamSpec::EStreamLatency streamSpec = CPulseStreamSpec::STREAM_LATENCY_INPUT_MID;
169         CPulseStreamSpec spec(streamSpec, mAudioInfo);
170
171         // Create PulseAudio Handler
172         mpPulseAudioClient = new CPulseAudioClient(CPulseAudioClient::STREAM_DIRECTION_RECORD, spec, this);
173         if (mpPulseAudioClient == NULL) {
174             THROW_ERROR_MSG(CAudioError::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::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::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::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::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::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::AUDIO_IO_STATE_RUNNING);
278     } catch (CAudioError e) {
279         internalUnlock();
280         throw e;
281     }
282 }
283
284 void CAudioInput::drain() throw (CAudioError) {
285     if (IsInit() == false || IsReady() == false) {
286         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
287     }
288
289     try{
290         CAudioIO::drain();
291     } catch (CAudioError e) {
292         internalUnlock();
293         throw e;
294     }
295 }
296
297 void CAudioInput::flush() throw (CAudioError) {
298     if (IsInit() == false || IsReady() == false) {
299         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
300     }
301
302     try {
303         CAudioIO::flush();
304     } catch (CAudioError e) {
305         internalUnlock();
306         throw e;
307     }
308 }
309
310 int CAudioInput::getBufferSize() throw (CAudioError) {
311     if (IsInit() == false) {
312         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput");
313     }
314
315     if (IsReady() == false) {
316         AUDIO_IO_LOGD("Warning: Did not prepare CAudioInput, then return zero");
317         return 0;
318     }
319
320     int size = 0;
321
322     try {
323         internalLock();
324         size = mpPulseAudioClient->getBufferSize();
325         internalUnlock();
326     } catch (CAudioError err) {
327         internalUnlock();
328         throw err;
329     }
330
331     return size;
332 }
333
334 void CAudioInput::setStreamCallback(SStreamCallback callback) throw (CAudioError) {
335     if (IsInit() == false) {
336         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize CAudioInput");
337     }
338
339     internalLock();
340     if (callback.onStream == NULL) {
341         AUDIO_IO_LOGD("mIsUsedSyncRead = true");
342         mIsUsedSyncRead = true;
343     } else {
344         AUDIO_IO_LOGD("mIsUsedSyncRead = false");
345         mIsUsedSyncRead = false;
346     }
347     internalUnlock();
348
349     CAudioIO::setStreamCallback(callback);
350 }
351
352 int CAudioInput::read(void* buffer, unsigned int length) throw (CAudioError) {
353     if (IsInit() == false || IsReady() == false) {
354         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
355     }
356
357     if (buffer == NULL) {
358         THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_INVALID_ARGUMENT, "Parameters are invalid - buffer:%p, length:%d", buffer, length);
359     }
360
361     /* Checks synchronous flag */
362     if (mIsUsedSyncRead == false) {
363         THROW_ERROR_MSG(CAudioError::ERROR_NOT_SUPPORTED, "Does not support read() if receive stream callback");
364     }
365
366     internalLock();
367
368     unsigned int lengthIter = length;
369     int ret = 0;
370
371     try {
372         while (lengthIter > 0) {
373             size_t l;
374
375             while (mpSyncReadDataPtr == NULL) {
376                 ret = mpPulseAudioClient->peek(&mpSyncReadDataPtr, &mSyncReadLength);
377                 if (ret != 0) {
378                     THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_INTERNAL_OPERATION, "Failed CPulseAudioClient::peek() ret:[%d]", ret);
379                 }
380
381                 if (mSyncReadLength <= 0) {
382 #ifdef _AUDIO_IO_DEBUG_TIMING_
383                     AUDIO_IO_LOGD("readLength(%d byte) is not valid.. wait..", mSyncReadLength);
384 #endif
385                     internalWait();
386                 } else if (mpSyncReadDataPtr == NULL) {
387                     /* There's a hole in the stream, skip it. We could generate
388                      * silence, but that wouldn't work for compressed streams.
389                      */
390                     ret = mpPulseAudioClient->drop();
391                     if (ret != 0) {
392                         THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_INTERNAL_OPERATION, "Failed CPulseAudioClient::drop() ret:[%d]", ret);
393                     }
394                 } else {
395                     mSyncReadIndex = 0;
396                 }
397             }//end of while (pReadData == NULL)
398
399             if (mSyncReadLength < lengthIter) {
400                 l = mSyncReadLength;
401             } else {
402                 l = lengthIter;
403             }
404
405             // Copy partial pcm data on out parameter
406 #ifdef _AUDIO_IO_DEBUG_TIMING_
407             AUDIO_IO_LOGD("memcpy() that a peeked buffer(%p), index(%d), length(%d), on out buffer", (const uint8_t*)(mpSyncReadDataPtr) + mSyncReadIndex, mSyncReadIndex, l);
408 #endif
409             memcpy(buffer, (const uint8_t*)mpSyncReadDataPtr + mSyncReadIndex, l);
410
411             // Move next position
412             buffer = (uint8_t*)buffer + l;
413             lengthIter -= l;
414
415             // Adjusts the rest length
416             mSyncReadIndex  += l;
417             mSyncReadLength -= l;
418
419             if (mSyncReadLength == 0) {
420 #ifdef _AUDIO_IO_DEBUG_TIMING_
421                 AUDIO_IO_LOGD("mSyncReadLength is zero - Do drop()");
422 #endif
423                 ret = mpPulseAudioClient->drop();
424                 if (ret != 0) {
425                     THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_INTERNAL_OPERATION, "Failed CPulseAudioClient::drop() ret:[%d]", ret);
426                 }
427
428                 // Reset the internal pointer
429                 mpSyncReadDataPtr = NULL;
430                 mSyncReadLength   = 0;
431                 mSyncReadIndex    = 0;
432             }
433         }//end of while (length > 0)
434     } catch (CAudioError e) {
435         internalUnlock();
436         throw e;
437     }
438
439     internalUnlock();
440
441     return length;
442 }
443
444 int CAudioInput::peek(const void** buffer, unsigned int* length) throw (CAudioError) {
445     if (IsInit() == false || IsReady() == false) {
446         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
447     }
448
449     if (buffer == NULL || length == NULL) {
450         THROW_ERROR_MSG_FORMAT(CAudioError::ERROR_INVALID_ARGUMENT, "Parameters are NULL buffer:%p, length:%p", buffer, length);
451     }
452
453     /* Checks synchronous flag */
454     internalLock();
455     if (mIsUsedSyncRead == true) {
456         THROW_ERROR_MSG(CAudioError::ERROR_NOT_SUPPORTED, "Does not support peek() if does not receive a stream callback");
457     }
458     internalUnlock();
459
460     int ret = 0;
461
462     try {
463         internalLock();
464         ret = mpPulseAudioClient->peek(buffer, length);
465         internalUnlock();
466     } catch (CAudioError e) {
467         internalUnlock();
468         throw e;
469     }
470
471     return ret;
472 }
473
474 int CAudioInput::drop() throw (CAudioError) {
475     if (IsInit() == false || IsReady() == false) {
476         THROW_ERROR_MSG(CAudioError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioInput");
477     }
478
479     /* Checks synchronous flag */
480     internalLock();
481     if (mIsUsedSyncRead == true) {
482         THROW_ERROR_MSG(CAudioError::ERROR_NOT_SUPPORTED, "Does not support drop() if does not receive a stream callback");
483     }
484     internalUnlock();
485
486     int ret = 0;
487
488     try {
489         internalLock();
490         ret = mpPulseAudioClient->drop();
491         internalUnlock();
492     } catch (CAudioError e) {
493         internalUnlock();
494         throw e;
495     }
496
497     return ret;
498 }