Home | History | Annotate | Download | only in android
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      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 "sles_allinclusive.h"
     19 #include "android_prompts.h"
     20 
     21 #include <system/audio.h>
     22 
     23 // use this flag to dump all recorded audio into a file
     24 //#define MONITOR_RECORDING
     25 #ifdef MONITOR_RECORDING
     26 #define MONITOR_TARGET "/sdcard/monitor.raw"
     27 #include <stdio.h>
     28 static FILE* gMonitorFp = NULL;
     29 #endif
     30 
     31 
     32 #define KEY_RECORDING_SOURCE_PARAMSIZE  sizeof(SLuint32)
     33 #define KEY_RECORDING_PRESET_PARAMSIZE  sizeof(SLuint32)
     34 
     35 //-----------------------------------------------------------------------------
     36 // Internal utility functions
     37 //----------------------------
     38 
     39 SLresult audioRecorder_setPreset(CAudioRecorder* ar, SLuint32 recordPreset) {
     40     SLresult result = SL_RESULT_SUCCESS;
     41 
     42     audio_source_t newRecordSource = AUDIO_SOURCE_DEFAULT;
     43     switch (recordPreset) {
     44     case SL_ANDROID_RECORDING_PRESET_GENERIC:
     45         newRecordSource = AUDIO_SOURCE_DEFAULT;
     46         break;
     47     case SL_ANDROID_RECORDING_PRESET_CAMCORDER:
     48         newRecordSource = AUDIO_SOURCE_CAMCORDER;
     49         break;
     50     case SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION:
     51         newRecordSource = AUDIO_SOURCE_VOICE_RECOGNITION;
     52         break;
     53     case SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION:
     54         newRecordSource = AUDIO_SOURCE_VOICE_COMMUNICATION;
     55         break;
     56     case SL_ANDROID_RECORDING_PRESET_NONE:
     57         // it is an error to set preset "none"
     58     default:
     59         SL_LOGE(ERROR_RECORDERPRESET_SET_UNKNOWN_PRESET);
     60         result = SL_RESULT_PARAMETER_INVALID;
     61     }
     62 
     63     // recording preset needs to be set before the object is realized
     64     // (ap->mAudioRecord is supposed to be NULL until then)
     65     if (SL_OBJECT_STATE_UNREALIZED != ar->mObject.mState) {
     66         SL_LOGE(ERROR_RECORDERPRESET_REALIZED);
     67         result = SL_RESULT_PRECONDITIONS_VIOLATED;
     68     } else {
     69         ar->mRecordSource = newRecordSource;
     70     }
     71 
     72     return result;
     73 }
     74 
     75 
     76 SLresult audioRecorder_getPreset(CAudioRecorder* ar, SLuint32* pPreset) {
     77     SLresult result = SL_RESULT_SUCCESS;
     78 
     79     switch (ar->mRecordSource) {
     80     case AUDIO_SOURCE_DEFAULT:
     81     case AUDIO_SOURCE_MIC:
     82         *pPreset = SL_ANDROID_RECORDING_PRESET_GENERIC;
     83         break;
     84     case AUDIO_SOURCE_VOICE_UPLINK:
     85     case AUDIO_SOURCE_VOICE_DOWNLINK:
     86     case AUDIO_SOURCE_VOICE_CALL:
     87         *pPreset = SL_ANDROID_RECORDING_PRESET_NONE;
     88         break;
     89     case AUDIO_SOURCE_VOICE_RECOGNITION:
     90         *pPreset = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
     91         break;
     92     case AUDIO_SOURCE_CAMCORDER:
     93         *pPreset = SL_ANDROID_RECORDING_PRESET_CAMCORDER;
     94         break;
     95     case AUDIO_SOURCE_VOICE_COMMUNICATION:
     96         *pPreset = SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION;
     97         break;
     98     default:
     99         *pPreset = SL_ANDROID_RECORDING_PRESET_NONE;
    100         result = SL_RESULT_INTERNAL_ERROR;
    101         break;
    102     }
    103 
    104     return result;
    105 }
    106 
    107 
    108 void audioRecorder_handleNewPos_lockRecord(CAudioRecorder* ar) {
    109     //SL_LOGV("received event EVENT_NEW_POS from AudioRecord");
    110     slRecordCallback callback = NULL;
    111     void* callbackPContext = NULL;
    112 
    113     interface_lock_shared(&ar->mRecord);
    114     callback = ar->mRecord.mCallback;
    115     callbackPContext = ar->mRecord.mContext;
    116     interface_unlock_shared(&ar->mRecord);
    117 
    118     if (NULL != callback) {
    119         // getting this event implies SL_RECORDEVENT_HEADATNEWPOS was set in the event mask
    120         (*callback)(&ar->mRecord.mItf, callbackPContext, SL_RECORDEVENT_HEADATNEWPOS);
    121     }
    122 }
    123 
    124 
    125 void audioRecorder_handleMarker_lockRecord(CAudioRecorder* ar) {
    126     //SL_LOGV("received event EVENT_MARKER from AudioRecord");
    127     slRecordCallback callback = NULL;
    128     void* callbackPContext = NULL;
    129 
    130     interface_lock_shared(&ar->mRecord);
    131     callback = ar->mRecord.mCallback;
    132     callbackPContext = ar->mRecord.mContext;
    133     interface_unlock_shared(&ar->mRecord);
    134 
    135     if (NULL != callback) {
    136         // getting this event implies SL_RECORDEVENT_HEADATMARKER was set in the event mask
    137         (*callback)(&ar->mRecord.mItf, callbackPContext, SL_RECORDEVENT_HEADATMARKER);
    138     }
    139 }
    140 
    141 
    142 void audioRecorder_handleOverrun_lockRecord(CAudioRecorder* ar) {
    143     //SL_LOGV("received event EVENT_OVERRUN from AudioRecord");
    144     slRecordCallback callback = NULL;
    145     void* callbackPContext = NULL;
    146 
    147     interface_lock_shared(&ar->mRecord);
    148     if (ar->mRecord.mCallbackEventsMask & SL_RECORDEVENT_HEADSTALLED) {
    149         callback = ar->mRecord.mCallback;
    150         callbackPContext = ar->mRecord.mContext;
    151     }
    152     interface_unlock_shared(&ar->mRecord);
    153 
    154     if (NULL != callback) {
    155         (*callback)(&ar->mRecord.mItf, callbackPContext, SL_RECORDEVENT_HEADSTALLED);
    156     }
    157 }
    158 
    159 //-----------------------------------------------------------------------------
    160 SLresult android_audioRecorder_checkSourceSinkSupport(CAudioRecorder* ar) {
    161 
    162     const SLDataSource *pAudioSrc = &ar->mDataSource.u.mSource;
    163     const SLDataSink   *pAudioSnk = &ar->mDataSink.u.mSink;
    164 
    165     // Sink check:
    166     // only buffer queue sinks are supported, regardless of the data source
    167     if (SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE != *(SLuint32 *)pAudioSnk->pLocator) {
    168         SL_LOGE(ERROR_RECORDER_SINK_MUST_BE_ANDROIDSIMPLEBUFFERQUEUE);
    169         return SL_RESULT_PARAMETER_INVALID;
    170     } else {
    171         // only PCM buffer queues are supported
    172         SLuint32 formatType = *(SLuint32 *)pAudioSnk->pFormat;
    173         if (SL_DATAFORMAT_PCM == formatType) {
    174             SLDataFormat_PCM *df_pcm = (SLDataFormat_PCM *)ar->mDataSink.u.mSink.pFormat;
    175             ar->mSampleRateMilliHz = df_pcm->samplesPerSec;
    176             ar->mNumChannels = df_pcm->numChannels;
    177             SL_LOGV("AudioRecorder requested sample rate = %u mHz, %u channel(s)",
    178                     ar->mSampleRateMilliHz, ar->mNumChannels);
    179         }
    180         else {
    181             SL_LOGE(ERROR_RECORDER_SINK_FORMAT_MUST_BE_PCM);
    182             return SL_RESULT_PARAMETER_INVALID;
    183         }
    184     }
    185 
    186     // Source check:
    187     // only input device sources are supported
    188     // check it's an IO device
    189     if (SL_DATALOCATOR_IODEVICE != *(SLuint32 *)pAudioSrc->pLocator) {
    190         SL_LOGE(ERROR_RECORDER_SOURCE_MUST_BE_IODEVICE);
    191         return SL_RESULT_PARAMETER_INVALID;
    192     } else {
    193 
    194         // check it's an input device
    195         SLDataLocator_IODevice *dl_iod = (SLDataLocator_IODevice *) pAudioSrc->pLocator;
    196         if (SL_IODEVICE_AUDIOINPUT != dl_iod->deviceType) {
    197             SL_LOGE(ERROR_RECORDER_IODEVICE_MUST_BE_AUDIOINPUT);
    198             return SL_RESULT_PARAMETER_INVALID;
    199         }
    200 
    201         // check it's the default input device, others aren't supported here
    202         if (SL_DEFAULTDEVICEID_AUDIOINPUT != dl_iod->deviceID) {
    203             SL_LOGE(ERROR_RECORDER_INPUT_ID_MUST_BE_DEFAULT);
    204             return SL_RESULT_PARAMETER_INVALID;
    205         }
    206     }
    207 
    208     return SL_RESULT_SUCCESS;
    209 }
    210 //-----------------------------------------------------------------------------
    211 static void audioRecorder_callback(int event, void* user, void *info) {
    212     //SL_LOGV("audioRecorder_callback(%d, %p, %p) entering", event, user, info);
    213 
    214     CAudioRecorder *ar = (CAudioRecorder *)user;
    215     void * callbackPContext = NULL;
    216 
    217     switch(event) {
    218     case android::AudioRecord::EVENT_MORE_DATA: {
    219         slBufferQueueCallback callback = NULL;
    220         android::AudioRecord::Buffer* pBuff = (android::AudioRecord::Buffer*)info;
    221 
    222         // push data to the buffer queue
    223         interface_lock_exclusive(&ar->mBufferQueue);
    224 
    225         if (ar->mBufferQueue.mState.count != 0) {
    226             assert(ar->mBufferQueue.mFront != ar->mBufferQueue.mRear);
    227 
    228             BufferHeader *oldFront = ar->mBufferQueue.mFront;
    229             BufferHeader *newFront = &oldFront[1];
    230 
    231             // FIXME handle 8bit based on buffer format
    232             short *pDest = (short*)((char *)oldFront->mBuffer + ar->mBufferQueue.mSizeConsumed);
    233             if (ar->mBufferQueue.mSizeConsumed + pBuff->size < oldFront->mSize) {
    234                 // can't consume the whole or rest of the buffer in one shot
    235                 ar->mBufferQueue.mSizeConsumed += pBuff->size;
    236                 // leave pBuff->size untouched
    237                 // consume data
    238                 // FIXME can we avoid holding the lock during the copy?
    239                 memcpy (pDest, pBuff->i16, pBuff->size);
    240 #ifdef MONITOR_RECORDING
    241                 if (NULL != gMonitorFp) { fwrite(pBuff->i16, pBuff->size, 1, gMonitorFp); }
    242 #endif
    243             } else {
    244                 // finish pushing the buffer or push the buffer in one shot
    245                 pBuff->size = oldFront->mSize - ar->mBufferQueue.mSizeConsumed;
    246                 ar->mBufferQueue.mSizeConsumed = 0;
    247                 if (newFront == &ar->mBufferQueue.mArray[ar->mBufferQueue.mNumBuffers + 1]) {
    248                     newFront = ar->mBufferQueue.mArray;
    249                 }
    250                 ar->mBufferQueue.mFront = newFront;
    251 
    252                 ar->mBufferQueue.mState.count--;
    253                 ar->mBufferQueue.mState.playIndex++;
    254                 // consume data
    255                 // FIXME can we avoid holding the lock during the copy?
    256                 memcpy (pDest, pBuff->i16, pBuff->size);
    257 #ifdef MONITOR_RECORDING
    258                 if (NULL != gMonitorFp) { fwrite(pBuff->i16, pBuff->size, 1, gMonitorFp); }
    259 #endif
    260                 // data has been copied to the buffer, and the buffer queue state has been updated
    261                 // we will notify the client if applicable
    262                 callback = ar->mBufferQueue.mCallback;
    263                 // save callback data
    264                 callbackPContext = ar->mBufferQueue.mContext;
    265             }
    266         } else {
    267             // no destination to push the data
    268             pBuff->size = 0;
    269         }
    270 
    271         interface_unlock_exclusive(&ar->mBufferQueue);
    272         // notify client
    273         if (NULL != callback) {
    274             (*callback)(&ar->mBufferQueue.mItf, callbackPContext);
    275         }
    276         }
    277         break;
    278 
    279     case android::AudioRecord::EVENT_OVERRUN:
    280         audioRecorder_handleOverrun_lockRecord(ar);
    281         break;
    282 
    283     case android::AudioRecord::EVENT_MARKER:
    284         audioRecorder_handleMarker_lockRecord(ar);
    285         break;
    286 
    287     case android::AudioRecord::EVENT_NEW_POS:
    288         audioRecorder_handleNewPos_lockRecord(ar);
    289         break;
    290 
    291     }
    292 }
    293 
    294 
    295 //-----------------------------------------------------------------------------
    296 SLresult android_audioRecorder_create(CAudioRecorder* ar) {
    297     SL_LOGV("android_audioRecorder_create(%p) entering", ar);
    298 
    299     const SLDataSource *pAudioSrc = &ar->mDataSource.u.mSource;
    300     const SLDataSink *pAudioSnk = &ar->mDataSink.u.mSink;
    301     SLresult result = SL_RESULT_SUCCESS;
    302 
    303     const SLuint32 sourceLocatorType = *(SLuint32 *)pAudioSrc->pLocator;
    304     const SLuint32 sinkLocatorType = *(SLuint32 *)pAudioSnk->pLocator;
    305 
    306     //  the following platform-independent fields have been initialized in CreateAudioRecorder()
    307     //    ar->mNumChannels
    308     //    ar->mSampleRateMilliHz
    309 
    310     if ((SL_DATALOCATOR_IODEVICE == sourceLocatorType) &&
    311             (SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE == sinkLocatorType)) {
    312         // microphone to simple buffer queue
    313         ar->mAndroidObjType = AUDIORECORDER_FROM_MIC_TO_PCM_BUFFERQUEUE;
    314         ar->mAudioRecord = NULL;
    315         ar->mRecordSource = AUDIO_SOURCE_DEFAULT;
    316     } else {
    317         result = SL_RESULT_CONTENT_UNSUPPORTED;
    318     }
    319 
    320     return result;
    321 }
    322 
    323 
    324 //-----------------------------------------------------------------------------
    325 SLresult android_audioRecorder_setConfig(CAudioRecorder* ar, const SLchar *configKey,
    326         const void *pConfigValue, SLuint32 valueSize) {
    327 
    328     SLresult result;
    329 
    330     assert(NULL != ar && NULL != configKey && NULL != pConfigValue);
    331     if (strcmp((const char*)configKey, (const char*)SL_ANDROID_KEY_RECORDING_PRESET) == 0) {
    332 
    333         // recording preset
    334         if (KEY_RECORDING_PRESET_PARAMSIZE > valueSize) {
    335             SL_LOGE(ERROR_CONFIG_VALUESIZE_TOO_LOW);
    336             result = SL_RESULT_BUFFER_INSUFFICIENT;
    337         } else {
    338             result = audioRecorder_setPreset(ar, *(SLuint32*)pConfigValue);
    339         }
    340 
    341     } else {
    342         SL_LOGE(ERROR_CONFIG_UNKNOWN_KEY);
    343         result = SL_RESULT_PARAMETER_INVALID;
    344     }
    345 
    346     return result;
    347 }
    348 
    349 
    350 //-----------------------------------------------------------------------------
    351 SLresult android_audioRecorder_getConfig(CAudioRecorder* ar, const SLchar *configKey,
    352         SLuint32* pValueSize, void *pConfigValue) {
    353 
    354     SLresult result;
    355 
    356     assert(NULL != ar && NULL != configKey && NULL != pValueSize);
    357     if (strcmp((const char*)configKey, (const char*)SL_ANDROID_KEY_RECORDING_PRESET) == 0) {
    358 
    359         // recording preset
    360         if (NULL == pConfigValue) {
    361             result = SL_RESULT_SUCCESS;
    362         } else if (KEY_RECORDING_PRESET_PARAMSIZE > *pValueSize) {
    363             SL_LOGE(ERROR_CONFIG_VALUESIZE_TOO_LOW);
    364             result = SL_RESULT_BUFFER_INSUFFICIENT;
    365         } else {
    366             result = audioRecorder_getPreset(ar, (SLuint32*)pConfigValue);
    367         }
    368         *pValueSize = KEY_RECORDING_PRESET_PARAMSIZE;
    369 
    370     } else {
    371         SL_LOGE(ERROR_CONFIG_UNKNOWN_KEY);
    372         result = SL_RESULT_PARAMETER_INVALID;
    373     }
    374 
    375     return result;
    376 }
    377 
    378 
    379 //-----------------------------------------------------------------------------
    380 SLresult android_audioRecorder_realize(CAudioRecorder* ar, SLboolean async) {
    381     SL_LOGV("android_audioRecorder_realize(%p) entering", ar);
    382 
    383     SLresult result = SL_RESULT_SUCCESS;
    384 
    385     // initialize platform-independent CAudioRecorder fields
    386     if (SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE != ar->mDataSink.mLocator.mLocatorType) {
    387         SL_LOGE(ERROR_RECORDER_SINK_MUST_BE_ANDROIDSIMPLEBUFFERQUEUE);
    388         return SL_RESULT_CONTENT_UNSUPPORTED;
    389     }
    390     //  the following platform-independent fields have been initialized in CreateAudioRecorder()
    391     //    ar->mNumChannels
    392     //    ar->mSampleRateMilliHz
    393 
    394     SL_LOGV("new AudioRecord %u channels, %u mHz", ar->mNumChannels, ar->mSampleRateMilliHz);
    395 
    396     // initialize platform-specific CAudioRecorder fields
    397     ar->mAudioRecord = new android::AudioRecord();
    398     ar->mAudioRecord->set(ar->mRecordSource, // source
    399             sles_to_android_sampleRate(ar->mSampleRateMilliHz), // sample rate in Hertz
    400             AUDIO_FORMAT_PCM_16_BIT,   //FIXME use format from buffer queue sink
    401             sles_to_android_channelMaskIn(ar->mNumChannels, 0 /*no channel mask*/),
    402                                    // channel config
    403             0,                     //frameCount min
    404             (android::AudioRecord::record_flags) 0, // flags
    405             audioRecorder_callback,// callback_t
    406             (void*)ar,             // user, callback data, here the AudioRecorder
    407             0,                     // notificationFrames
    408             false);                // threadCanCallJava, note: this will prevent direct Java
    409                                    //   callbacks, but we don't want them in the recording loop
    410 
    411     if (android::NO_ERROR != ar->mAudioRecord->initCheck()) {
    412         SL_LOGE("android_audioRecorder_realize(%p) error creating AudioRecord object", ar);
    413         result = SL_RESULT_CONTENT_UNSUPPORTED;
    414     }
    415 
    416 #ifdef MONITOR_RECORDING
    417     gMonitorFp = fopen(MONITOR_TARGET, "w");
    418     if (NULL == gMonitorFp) { SL_LOGE("error opening %s", MONITOR_TARGET); }
    419     else { SL_LOGE("recording to %s", MONITOR_TARGET); } // SL_LOGE so it's always displayed
    420 #endif
    421 
    422     return result;
    423 }
    424 
    425 
    426 //-----------------------------------------------------------------------------
    427 void android_audioRecorder_destroy(CAudioRecorder* ar) {
    428     SL_LOGV("android_audioRecorder_destroy(%p) entering", ar);
    429 
    430     if (NULL != ar->mAudioRecord) {
    431         ar->mAudioRecord->stop();
    432         delete ar->mAudioRecord;
    433         ar->mAudioRecord = NULL;
    434     }
    435 
    436 #ifdef MONITOR_RECORDING
    437     if (NULL != gMonitorFp) {
    438         fclose(gMonitorFp);
    439         gMonitorFp = NULL;
    440     }
    441 #endif
    442 }
    443 
    444 
    445 //-----------------------------------------------------------------------------
    446 void android_audioRecorder_setRecordState(CAudioRecorder* ar, SLuint32 state) {
    447     SL_LOGV("android_audioRecorder_setRecordState(%p, %u) entering", ar, state);
    448 
    449     if (NULL == ar->mAudioRecord) {
    450         return;
    451     }
    452 
    453     switch (state) {
    454      case SL_RECORDSTATE_STOPPED:
    455          ar->mAudioRecord->stop();
    456          break;
    457      case SL_RECORDSTATE_PAUSED:
    458          // Note that pausing is treated like stop as this implementation only records to a buffer
    459          //  queue, so there is no notion of destination being "opened" or "closed" (See description
    460          //  of SL_RECORDSTATE in specification)
    461          ar->mAudioRecord->stop();
    462          break;
    463      case SL_RECORDSTATE_RECORDING:
    464          ar->mAudioRecord->start();
    465          break;
    466      default:
    467          break;
    468      }
    469 
    470 }
    471 
    472 
    473 //-----------------------------------------------------------------------------
    474 void android_audioRecorder_useRecordEventMask(CAudioRecorder *ar) {
    475     IRecord *pRecordItf = &ar->mRecord;
    476     SLuint32 eventFlags = pRecordItf->mCallbackEventsMask;
    477 
    478     if (NULL == ar->mAudioRecord) {
    479         return;
    480     }
    481 
    482     if ((eventFlags & SL_RECORDEVENT_HEADATMARKER) && (pRecordItf->mMarkerPosition != 0)) {
    483         ar->mAudioRecord->setMarkerPosition((uint32_t)((((int64_t)pRecordItf->mMarkerPosition
    484                 * sles_to_android_sampleRate(ar->mSampleRateMilliHz)))/1000));
    485     } else {
    486         // clear marker
    487         ar->mAudioRecord->setMarkerPosition(0);
    488     }
    489 
    490     if (eventFlags & SL_RECORDEVENT_HEADATNEWPOS) {
    491         SL_LOGV("pos update period %d", pRecordItf->mPositionUpdatePeriod);
    492          ar->mAudioRecord->setPositionUpdatePeriod(
    493                 (uint32_t)((((int64_t)pRecordItf->mPositionUpdatePeriod
    494                 * sles_to_android_sampleRate(ar->mSampleRateMilliHz)))/1000));
    495     } else {
    496         // clear periodic update
    497         ar->mAudioRecord->setPositionUpdatePeriod(0);
    498     }
    499 
    500     if (eventFlags & SL_RECORDEVENT_HEADATLIMIT) {
    501         // FIXME support SL_RECORDEVENT_HEADATLIMIT
    502         SL_LOGD("[ FIXME: IRecord_SetCallbackEventsMask(SL_RECORDEVENT_HEADATLIMIT) on an "
    503                     "SL_OBJECTID_AUDIORECORDER to be implemented ]");
    504     }
    505 
    506     if (eventFlags & SL_RECORDEVENT_HEADMOVING) {
    507         // FIXME support SL_RECORDEVENT_HEADMOVING
    508         SL_LOGD("[ FIXME: IRecord_SetCallbackEventsMask(SL_RECORDEVENT_HEADMOVING) on an "
    509                 "SL_OBJECTID_AUDIORECORDER to be implemented ]");
    510     }
    511 
    512     if (eventFlags & SL_RECORDEVENT_BUFFER_FULL) {
    513         // nothing to do for SL_RECORDEVENT_BUFFER_FULL since this will not be encountered on
    514         // recording to buffer queues
    515     }
    516 
    517     if (eventFlags & SL_RECORDEVENT_HEADSTALLED) {
    518         // nothing to do for SL_RECORDEVENT_HEADSTALLED, callback event will be checked against mask
    519         // when AudioRecord::EVENT_OVERRUN is encountered
    520 
    521     }
    522 
    523 }
    524 
    525 
    526 //-----------------------------------------------------------------------------
    527 void android_audioRecorder_getPosition(CAudioRecorder *ar, SLmillisecond *pPosMsec) {
    528     if ((NULL == ar) || (NULL == ar->mAudioRecord)) {
    529         *pPosMsec = 0;
    530     } else {
    531         uint32_t positionInFrames;
    532         ar->mAudioRecord->getPosition(&positionInFrames);
    533         if (ar->mSampleRateMilliHz == UNKNOWN_SAMPLERATE) {
    534             *pPosMsec = 0;
    535         } else {
    536             *pPosMsec = ((int64_t)positionInFrames * 1000) /
    537                     sles_to_android_sampleRate(ar->mSampleRateMilliHz);
    538         }
    539     }
    540 }
    541