/* Copyright Statement: * * This software/firmware and related documentation ("MediaTek Software") are * protected under relevant copyright laws. The information contained herein is * confidential and proprietary to MediaTek Inc. and/or its licensors. Without * the prior written permission of MediaTek inc. and/or its licensors, any * reproduction, modification, use or disclosure of MediaTek Software, and * information contained herein, in whole or in part, shall be strictly * prohibited. * * MediaTek Inc. (C) 2010. All rights reserved. * * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE") * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER * ON AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR * NONINFRINGEMENT. NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, * INCORPORATED IN, OR SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN MEDIATEK * SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE * RELEASED HEREUNDER WILL BE, AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE * MEDIATEK SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE * CHARGE PAID BY RECEIVER TO MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE. * * The following software/firmware and/or related documentation ("MediaTek * Software") have been modified by MediaTek Inc. All revisions are subject to * any receiver's applicable license agreements with MediaTek Inc. */ #define LOG_TAG "AudioUtilmtk" #include #include #include "AudioParamParser.h" #define MTK_LOG_ENABLE 1 #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { KeyedVector mAudioDumpKeyEnableVector; Mutex mAudioDumpKeyMutex; static const char * af_dump_log = "vendor.af.dumplog"; const char * const AudioDump::keyAudioDumpMixer = "vendor.af.mixer.pcm"; const char * const AudioDump::keyAudioDumpTrack = "vendor.af.track.pcm"; const char * const AudioDump::keyAudioDumpOffload = "vendor.af.offload.write.raw"; const char * const AudioDump::keyAudioDumpResampler = "vendor.af.resampler.pcm"; const char * const AudioDump::keyAudioDumpMixerEnd = "vendor.af.mixer.end.pcm"; const char * const AudioDump::keyAudioDumpRecord = "vendor.af.record.dump.pcm"; const char * const AudioDump::keyAudioDumpEffect = "vendor.af.effect.pcm"; const char * const AudioDump::keyAudioDumpDrc = "vendor.af.mixer.drc.pcm"; const char * const AudioDump::keyAudioDumpLog = "vendor.af.dumplog"; const char * const AudioDump::keyAudioDumpAAudio = "vendor.aaudio.pcm"; const char * AudioDump::audioDumpPropertyStr[] = { AudioDump::keyAudioDumpMixer, AudioDump::keyAudioDumpTrack, AudioDump::keyAudioDumpOffload, AudioDump::keyAudioDumpResampler, AudioDump::keyAudioDumpMixerEnd, AudioDump::keyAudioDumpRecord, AudioDump::keyAudioDumpEffect, AudioDump::keyAudioDumpDrc, AudioDump::keyAudioDumpLog, AudioDump::keyAudioDumpAAudio, }; const char * const AudioDump::af_track_pcm = "/data/debuglogger/audio_dump/af_track_pcm"; const char * const AudioDump::af_mixer_write_pcm = "/data/debuglogger/audio_dump/af_mixer_write"; const char * const AudioDump::af_mixer_end_pcm = "/data/debuglogger/audio_dump/mixer_end"; const char * const AudioDump::af_offload_write_raw = "/data/debuglogger/audio_dump/af_offload_write_raw"; const char * const AudioDump::af_record_read_pcm = "/data/debuglogger/audio_dump/af_record_read"; const char * const AudioDump::af_record_convert_pcm = "/data/debuglogger/audio_dump/af_record_convert"; const char * const AudioDump::af_effect_pcm = "/data/debuglogger/audio_dump/af_effect_pcm"; const char * const AudioDump::af_mixer_drc_pcm_before = "/data/debuglogger/audio_dump/mixer_drc_before"; const char * const AudioDump::af_mixer_drc_pcm_after = "/data/debuglogger/audio_dump/mixer_drc_after"; const char * const AudioDump::af_resampler_in_pcm = "/data/debuglogger/audio_dump/af_mixer_resampler_in"; const char * const AudioDump::af_resampler_out_pcm = "/data/debuglogger/audio_dump/af_mixer_resampler_out"; const char * const AudioDump::aaudio_share_dl = "/data/debuglogger/audio_dump/aaudio_share_dl"; const char * const AudioDump::aaudio_share_ul = "/data/debuglogger/audio_dump/aaudio_share_ul"; const char * const AudioDump::aaudio_exclusive_dl = "/data/debuglogger/audio_dump/aaudio_exclusive_dl"; const char * const AudioDump::aaudio_exclusive_ul = "/data/debuglogger/audio_dump/aaudio_exclusive_ul"; bool FeatureOption::MTK_AUDIOMIXER_ENABLE_DRC = false; int FeatureOption:: MTK_ENABLE_STEREO_SPEAKER = 0; bool FeatureOption::MTK_USB_PHONECALL = true; bool FeatureOption::MTK_TTY_SUPPORT = false; bool FeatureOption::MTK_HIFIAUDIO_SUPPORT = false; bool FeatureOption::MTK_BESLOUDNESS_ENABLE = false; bool FeatureOption::MTK_AUDIO_GAIN_TABLE = false; bool FeatureOption::MTK_AUDIO_GAIN_NVRAM = false; bool FeatureOption::MTK_FM_ENABLE = true; bool FeatureOption::MTK_AURISYS_FRAMEWORK_SUPPORT = false; bool FeatureOption::MTK_BT_HEARING_AID_SUPPORT = false; bool FeatureOption::MTK_BT_HEARING_AID_USING_AOSP_FMW = false; bool FeatureOption::MTK_TC10_FEATURE = false; bool FeatureOption::MTK_TC10_IN_HOUSE = false; bool FeatureOption::MTK_AUDIO_A2DP_LATENCY_IMPROVE = false; bool FeatureOption::MTK_MUSIC_ROUTE_TO_BTSCO_DURING_BTSCO_ON = true; bool FeatureOption::MTK_BLE_PHONECALL = true; bool FeatureOption::MTK_CRS_FEATURE = true; const char * const FeatureOption::foName_MTK_BESLOUDNESS_RUN_WITH_HAL = "MTK_BESLOUDNESS_RUN_WITH_HAL"; const char * const FeatureOption::foName_MTK_BESLOUDNESS_SUPPORT = "MTK_BESLOUDNESS_SUPPORT"; const char * const FeatureOption::foName_MTK_AUDIO_NUMBER_OF_SPEAKER = "MTK_AUDIO_NUMBER_OF_SPEAKER"; const char * const FeatureOption::foName_MTK_AUDIO_HIERARCHICAL_PARAM_SUPPORT = "MTK_AUDIO_HIERARCHICAL_PARAM_SUPPORT"; const char * const FeatureOption::foName_MTK_HIFIAUDIO_SUPPORT = "MTK_HIFIAUDIO_SUPPORT"; const char * const FeatureOption::foName_MTK_AURISYS_FRAMEWORK_SUPPORT = "MTK_AURISYS_FRAMEWORK_SUPPORT"; const char * const FeatureOption::foName_MTK_USB_PHONECALL = "MTK_USB_PHONECALL"; const char * const FeatureOption::foName_MTK_TTY_SUPPORT = "MTK_TTY_SUPPORT"; const char * const FeatureOption::foName_MTK_BT_HEARING_AID_SUPPORT = "MTK_BT_HEARING_AID_SUPPORT"; const char * const FeatureOption::foName_MTK_BT_HEARING_AID_USING_AOSP_FMW = "MTK_BT_HEARING_AID_USING_AOSP_FMW"; const char * const FeatureOption::foName_MTK_TC10_FEATURE = "MTK_TC10_FEATURE"; const char * const FeatureOption::foName_MTK_TC10_IN_HOUSE = "MTK_TC10_IN_HOUSE"; const char * const FeatureOption::foName_MTK_AUDIO_A2DP_LATENCY_IMPROVE = "MTK_AUDIO_A2DP_LATENCY_IMPROVE"; const char * const FeatureOption::foName_MTK_MUSIC_ROUTE_TO_BTSCO_DURING_BTSCO_ON = "MTK_MUSIC_ROUTE_TO_BTSCO_DURING_BTSCO_ON"; const char * const FeatureOption::foName_MTK_BLE_PHONECALL = "MTK_BLE_PHONECALL"; struct WAVEFORMATEX { uint16_t wFormatTag; uint16_t nChannels; uint32_t nSamplesPerSec; uint32_t nAvgBytesPerSec; uint16_t nBlockAlign; uint16_t wBitsPerSample; uint16_t cbSize; WAVEFORMATEX() { wFormatTag = 0; nChannels = 0; nSamplesPerSec = 0; nAvgBytesPerSec = 0; nBlockAlign = 0; wBitsPerSample = 0; cbSize = 0; } }; struct WavFormatHeader { char ckID[5]; // 4 : Chunk ID: "RIFF" uint32_t cksize; // 4: Chunk size: File length - 8 char WAVEID[5]; // 4: WAVE ID: "WAVE" // Format Chunk char FormatckID[5]; // 4: "fmt " uint32_t Formatcksize; // 4: Chunk size: 16 or 18 or 40 ( We will use 18, no extensiable format. ) char DataID[5]; // 4: "data" uint32_t Datacksize; // 4: Chunk size: Data Size WAVEFORMATEX WaveFormatEx; WavFormatHeader() : cksize(0), Formatcksize(18), Datacksize(0) { strncpy(ckID, "RIFF", sizeof(ckID)); strncpy(WAVEID, "WAVE", sizeof(WAVEID)); strncpy(FormatckID, "fmt ", sizeof(FormatckID)); strncpy(DataID, "data", sizeof(DataID)); } }; // This structure will be written to the file header struct AudioDumpFileInfo { audio_format_t format; uint32_t sampleRate; uint32_t channelCount; int size; AudioDumpFileInfo() { format = AUDIO_FORMAT_INVALID; sampleRate = 0; channelCount = 0; size = 0; } }; struct AudioDumpBuffer { void *pBufBase; int changeCount; // The serial number of the file int allocBufSize; AudioDumpFileInfo fileInfo; AudioDumpBuffer() { pBufBase = NULL; changeCount = 0; allocBufSize = 0; } }; #define AUDIO_DUMP_BUFFER_COUNT_MAX 32 // How many buffer will combine #define AUDIO_DUMP_FILE_WAV_HEADER_SIZE 46 //46 is wav header pthread_t hAudioDumpThread = 0; pthread_cond_t AudioDataNotifyEvent; pthread_mutex_t AudioDataNotifyMutex; Mutex mAudioDumpMutex; Mutex mAudioDumpFileMutex; KeyedVector* > mAudioDumpFileVector; ///< The queue buffer waiting for write ///< The first element of Vector is the previous buffer info. KeyedVector* > mAudioDumpQueueVector; #define AUDIO_DUMP_FILE_DELAY_TIME_KEY "audiodump.filedelaytime" int mAudioDumpSleepTime = 2; uint32_t mAudioDumpFileIoDelayTime = 0; FILE* fopen_rb(String8 filePath) { FILE * fp = fopen(filePath.string(), "rb+"); if (mAudioDumpFileIoDelayTime > 0) { usleep(mAudioDumpFileIoDelayTime * 1000); } if (fp == NULL) { ALOGV("fopen_rb() file(%s) rb+ fail, open with ab+", filePath.string()); fp= fopen(filePath.string(), "ab+"); if (mAudioDumpFileIoDelayTime > 0) { usleep(mAudioDumpFileIoDelayTime * 1000); } } if (fp == NULL) { // ALOGE("fopen_rb() file(%s) fail", filePath.string()); return NULL; } return fp; } size_t fwrite_s(const void * ptr, size_t size, size_t count, FILE * stream) { size_t ret = fwrite(ptr, size, count, stream); if (ret > 0 && mAudioDumpFileIoDelayTime > 0) { usleep(mAudioDumpFileIoDelayTime * 1000); } return ret; } void fseek_s(FILE * stream, long int offset, int origin) { int ret = fseek(stream, offset, origin); if (ret == 0 && mAudioDumpFileIoDelayTime > 0) { usleep(mAudioDumpFileIoDelayTime * 1000); } } size_t fread_s (void * ptr, size_t size, size_t count, FILE * stream) { size_t ret = fread(ptr, size, count, stream); if (ret > 0 && mAudioDumpFileIoDelayTime > 0) { usleep(mAudioDumpFileIoDelayTime * 1000); } return ret; } void WriteNewWaveHeader(FILE *fp, AudioDumpBuffer audioBuffer) { WavFormatHeader wavHeader; void* tmpBuffer = malloc(46); if(!tmpBuffer) { ALOGE("%s(): malloc fail!", __FUNCTION__); return; } if (audioBuffer.fileInfo.format == AUDIO_FORMAT_PCM_FLOAT) { wavHeader.WaveFormatEx.wFormatTag = 3; // IEEE Float wavHeader.WaveFormatEx.wBitsPerSample = 32; } else { wavHeader.WaveFormatEx.wFormatTag = 1; // PCM if (audioBuffer.fileInfo.format == AUDIO_FORMAT_PCM_8_BIT) { wavHeader.WaveFormatEx.wBitsPerSample = 8; } else if (audioBuffer.fileInfo.format == AUDIO_FORMAT_PCM_16_BIT) { wavHeader.WaveFormatEx.wBitsPerSample = 16; } else if (audioBuffer.fileInfo.format == AUDIO_FORMAT_PCM_24_BIT_PACKED) { wavHeader.WaveFormatEx.wBitsPerSample = 24; } else if (audioBuffer.fileInfo.format == AUDIO_FORMAT_PCM_8_24_BIT || audioBuffer.fileInfo.format == AUDIO_FORMAT_PCM_32_BIT) { wavHeader.WaveFormatEx.wBitsPerSample = 32; } } wavHeader.cksize = audioBuffer.fileInfo.size + 38; // 46 - 8 wavHeader.Datacksize = audioBuffer.fileInfo.size; wavHeader.WaveFormatEx.nChannels = audioBuffer.fileInfo.channelCount; wavHeader.WaveFormatEx.nSamplesPerSec = audioBuffer.fileInfo.sampleRate; wavHeader.WaveFormatEx.nAvgBytesPerSec = wavHeader.WaveFormatEx.nSamplesPerSec * wavHeader.WaveFormatEx.nChannels * wavHeader.WaveFormatEx.wBitsPerSample / 8; wavHeader.WaveFormatEx.nBlockAlign = wavHeader.WaveFormatEx.nChannels * wavHeader.WaveFormatEx.wBitsPerSample / 8; wavHeader.WaveFormatEx.cbSize = 0; int pos = 0; memcpy((char*)tmpBuffer + pos, &wavHeader.ckID, 4); pos += 4; memcpy((char*)tmpBuffer + pos, &wavHeader.cksize, 4); pos += 4; memcpy((char*)tmpBuffer + pos, &wavHeader.WAVEID, 4); pos += 4; memcpy((char*)tmpBuffer + pos, &wavHeader.FormatckID, 4); pos += 4; memcpy((char*)tmpBuffer + pos, &wavHeader.Formatcksize, 4); pos += 4; memcpy((char*)tmpBuffer + pos, &wavHeader.WaveFormatEx, 18); pos += 18; memcpy((char*)tmpBuffer + pos, &wavHeader.DataID, 4); pos += 4; memcpy((char*)tmpBuffer + pos, &wavHeader.Datacksize, 4); pos += 4; fwrite_s(tmpBuffer, 46, 1, fp); free(tmpBuffer); tmpBuffer = NULL; } void UpdateWaveHeader(FILE *fp, AudioDumpBuffer audioBuffer) { fseek_s(fp, 0, SEEK_END); if (ftell(fp) == 0) { WriteNewWaveHeader(fp, audioBuffer); } else { WavFormatHeader wavHeader; void* tmpBuffer = malloc(46); if(!tmpBuffer) { ALOGE("%s(): malloc fail!", __FUNCTION__); return; } fseek_s(fp, 0, SEEK_SET); if (fread_s((char*)tmpBuffer, 46, 1, fp) <= 0) { free(tmpBuffer); tmpBuffer = NULL; return; } memcpy((char*)(&wavHeader.cksize), (char*)tmpBuffer + 4, 4); wavHeader.cksize += audioBuffer.fileInfo.size; memcpy((char*)tmpBuffer + 4, &wavHeader.cksize, 4); memcpy((char*)(&wavHeader.Datacksize), (char*)tmpBuffer + 42, 4); wavHeader.Datacksize += audioBuffer.fileInfo.size; memcpy((char*)tmpBuffer + 42, &wavHeader.Datacksize, 4); fseek_s(fp, 0, SEEK_SET); fwrite_s((char*)tmpBuffer, 46, 1, fp); free(tmpBuffer); tmpBuffer = NULL; } } void UpdateWaveHeader_f(String8 filePath, AudioDumpBuffer audioBuffer) { FILE *fp = fopen_rb(filePath); if (fp != NULL) { //ALOGD("UpdateWaveHeader_f START filePath(%s)", filePath.string()); UpdateWaveHeader(fp, audioBuffer); if (fclose(fp) != 0) { ALOGE("%s(): Couldn't close dump file", __FUNCTION__); } } } void WriteAudioBuffer(FILE *fp, AudioDumpBuffer audioBuffer) { fwrite_s(audioBuffer.pBufBase, audioBuffer.fileInfo.size, 1, fp); //ALOGD("free %p, %d", audioBuffer.pBufBase, audioBuffer.fileInfo.size); free(audioBuffer.pBufBase); audioBuffer.pBufBase = NULL; } void UpdateAllWaveHeader() { //int FileVectorSize = mAudioDumpFileVector.size(); for (size_t i = 0; i < mAudioDumpFileVector.size(); i++) { String8 filePathPCM = mAudioDumpFileVector.keyAt(i); Vector*pvector = (mAudioDumpFileVector.valueAt(i)); int BufferVectorSize = (*pvector).size(); if (BufferVectorSize == 1) { // Only Header info. String8 filePathWav = filePathPCM; AudioDumpBuffer *pLastBufferInfo = (*pvector)[0]; if (pLastBufferInfo->changeCount == 0) { filePathWav = String8::format("%s.wav", filePathPCM.string()); } else { filePathWav = String8::format("%s.%d.wav", filePathPCM.string(), pLastBufferInfo->changeCount); } UpdateWaveHeader_f(filePathWav, *pLastBufferInfo); bool bDeleted = false; if ((*pvector).size() == 1) { (*pvector).removeAt(0); delete pLastBufferInfo; pLastBufferInfo = NULL; mAudioDumpFileVector.removeItem(filePathPCM); delete pvector; pvector = NULL; bDeleted = true; i--; } if (bDeleted) { //break; // Break cause the vector size is changed. } } } } void PushBufferFromeQueueToVector(String8 filePath, AudioDumpBuffer *newDumpBuffer) { AudioDumpBuffer *newInBuffer = newDumpBuffer; Vector *pDumpBufferVector = NULL; ssize_t index = mAudioDumpFileVector.indexOfKey(filePath); if (index < 0) { pDumpBufferVector = new Vector; mAudioDumpFileVector.add(filePath, pDumpBufferVector); } else { pDumpBufferVector = mAudioDumpFileVector.valueAt(index); } if (pDumpBufferVector->size() == 0) { // No previous buffer info, create a new one. AudioDumpBuffer *lastInfoBuffer = new AudioDumpBuffer(); memcpy(lastInfoBuffer, newInBuffer, sizeof(AudioDumpBuffer)); lastInfoBuffer->pBufBase = NULL; // We don't care this parameter in last info. lastInfoBuffer->fileInfo.size = 0; // The size that had writtn to file. Set to 0 here since we havn't write anything. pDumpBufferVector->add(lastInfoBuffer); } pDumpBufferVector->add(newInBuffer); } void ProcessBufferQueue(bool bFlush) { if (AUDIO_DUMP_BUFFER_COUNT_MAX == 1) { bFlush = true; } Vector *pQueueBufferVector = NULL; Vector keyVector; mAudioDumpMutex.lock(); int size = mAudioDumpQueueVector.size(); for (int i = 0; i < size; i++) { String8 key = mAudioDumpQueueVector.keyAt(i); keyVector.add(key); } mAudioDumpMutex.unlock(); for (size_t i = 0; i < keyVector.size(); i++) { int allocBufSize = 0; int currBufSize = 0; int mergeBufSize = 0; mAudioDumpMutex.lock(); pQueueBufferVector = mAudioDumpQueueVector.valueFor(keyVector[i]); int queueSize = (*pQueueBufferVector).size(); mAudioDumpMutex.unlock(); while (queueSize >= 2) { mAudioDumpMutex.lock(); AudioDumpBuffer *newQueueBuffer = (*pQueueBufferVector)[0]; AudioDumpBuffer *newQueueBuffer2 = (*pQueueBufferVector)[1]; mAudioDumpMutex.unlock(); if (memcmp(&(newQueueBuffer->fileInfo), &(newQueueBuffer2->fileInfo), sizeof(AudioDumpFileInfo) - sizeof(int)) != 0) { // Compare format/sample rate/channel, excluding size PushBufferFromeQueueToVector(keyVector[i], newQueueBuffer); mAudioDumpMutex.lock(); pQueueBufferVector->removeAt(0); mAudioDumpMutex.unlock(); } else { currBufSize = newQueueBuffer->fileInfo.size; mergeBufSize = newQueueBuffer2->fileInfo.size; // allocate a big buffer to merge data if (newQueueBuffer->allocBufSize == 0) { void *pBufTemp = newQueueBuffer->pBufBase; allocBufSize = currBufSize * AUDIO_DUMP_BUFFER_COUNT_MAX; newQueueBuffer->allocBufSize = allocBufSize; newQueueBuffer->pBufBase = (char*)malloc(allocBufSize); if (newQueueBuffer->pBufBase != NULL) { memcpy((char*)newQueueBuffer->pBufBase, pBufTemp, currBufSize); } else { newQueueBuffer->allocBufSize = 0; //error handle? ALOGE("%s(): Error! Not enough memory for audio dump buffer size=%d", __FUNCTION__, allocBufSize); } free(pBufTemp); pBufTemp = NULL; } if ((newQueueBuffer->pBufBase != NULL) && (newQueueBuffer->allocBufSize >= currBufSize + mergeBufSize)) { memcpy((char*)(newQueueBuffer->pBufBase) + currBufSize, (char*)(newQueueBuffer2->pBufBase), mergeBufSize); newQueueBuffer->fileInfo.size += mergeBufSize; mAudioDumpMutex.lock(); pQueueBufferVector->removeAt(1); mAudioDumpMutex.unlock(); free(newQueueBuffer2->pBufBase); newQueueBuffer2->pBufBase = NULL; delete newQueueBuffer2; newQueueBuffer2 = NULL; } else { PushBufferFromeQueueToVector(keyVector[i], newQueueBuffer); mAudioDumpMutex.lock(); pQueueBufferVector->removeAt(0); mAudioDumpMutex.unlock(); } } queueSize--; } if (bFlush && queueSize == 1) { //ALOGD("Flush data"); mAudioDumpMutex.lock(); AudioDumpBuffer *newQueueBuffer = (*pQueueBufferVector)[0]; mAudioDumpMutex.unlock(); PushBufferFromeQueueToVector(keyVector[i], newQueueBuffer); mAudioDumpMutex.lock(); pQueueBufferVector->removeItemsAt(0); mAudioDumpMutex.unlock(); queueSize--; } } } void *AudioDumpThread(void *arg __attribute__((unused))) { pthread_detach(pthread_self()); bool bHasdata = false; bool bFlush = false; int iNoDataCount = 0; int iLoopCount = 0; ALOGV("AudioDumpThread(%ld) start", hAudioDumpThread); id_t tid = gettid(); int priority = -10; int ret; //ret = getpriority(PRIO_PROCESS, tid); //ALOGD("Thread(%d) priority = %d", tid, ret); ret = setpriority(PRIO_PROCESS, tid, priority); if (ret != 0) { ALOGE("AudioDumpThread setpriority fail. tid(%d), priority(%d) ", tid, priority); } //ret = getpriority(PRIO_PROCESS, tid); //ALOGD("Thread(%d) priority = %d", tid, ret); while (1) { ProcessBufferQueue(bFlush); bFlush = false; bHasdata = false; size_t FileVectorSize = mAudioDumpFileVector.size(); for (size_t i = 0; i < FileVectorSize; i++) { String8 filePathPCM = mAudioDumpFileVector.keyAt(i); Vector* pvector = (mAudioDumpFileVector.valueAt(i)); int BufferVectorSize = (*pvector).size(); if (BufferVectorSize > 1) { bHasdata = true; FILE * fpWav = NULL; String8 filePathWav = filePathPCM; AudioDumpBuffer *pLastBufferInfo = (*pvector)[0]; if (pLastBufferInfo == NULL) { ALOGE("Array index error!!![%s]", filePathPCM.string()); continue; } //get the dump file name if (pLastBufferInfo->changeCount == 0) { filePathWav = String8::format("%s.wav", filePathPCM.string()); } else { filePathWav = String8::format("%s.%d.wav", filePathPCM.string(), pLastBufferInfo->changeCount); } // create dump folder if need int ret = AudioDump::checkPath(filePathWav.string()); if (ret < 0) { ALOGE("dump %s fail!!!", filePathWav.string()); continue; } while (BufferVectorSize > 1) { AudioDumpBuffer *pAudioBuffer = (*pvector)[1]; if (pAudioBuffer == NULL || pLastBufferInfo == NULL) { //ALOGE("AudioDumpThread null buffer error!!!!"); break; } // if the format is changed, we use a new file name if (memcmp(&(pAudioBuffer->fileInfo), &(pLastBufferInfo->fileInfo), sizeof(AudioDumpFileInfo) - sizeof(int)) != 0) { if (fpWav != NULL) { if (fclose(fpWav) != 0) { ALOGE("%s(): Couldn't close dump file", __FUNCTION__); } fpWav = NULL; } UpdateWaveHeader_f(filePathWav, *pLastBufferInfo); int changeCount = pLastBufferInfo->changeCount + 1; memcpy(pLastBufferInfo, pAudioBuffer, sizeof(AudioDumpBuffer)); pLastBufferInfo->changeCount = changeCount; pLastBufferInfo->fileInfo.size = 0; filePathWav = String8::format("%s.%d.wav", filePathPCM.string(), pLastBufferInfo->changeCount); } // open the dump file, and write new header if need if (fpWav == NULL) { fpWav = fopen(filePathWav.string(), "ab+"); if (fpWav != NULL) { fseek_s(fpWav, 0, SEEK_END); if (ftell(fpWav) == 0) { // Write Header WriteNewWaveHeader(fpWav, *pLastBufferInfo); } } } // write audio buffer if (fpWav != NULL) { WriteAudioBuffer(fpWav, *pAudioBuffer); pLastBufferInfo->fileInfo.size += pAudioBuffer->fileInfo.size; (*pvector).removeAt(1); delete pAudioBuffer; pAudioBuffer = NULL; BufferVectorSize--; } } if (fpWav != NULL) { if (fclose(fpWav) != 0) { ALOGE("%s(): Couldn't close dump file.", __FUNCTION__); } fpWav = NULL; } } } if (!bHasdata) { iNoDataCount++; if (iNoDataCount >= 2 * AUDIO_DUMP_BUFFER_COUNT_MAX) { // wait 640ms bFlush = true; iLoopCount = 0; } if (iNoDataCount >= 2 * AUDIO_DUMP_BUFFER_COUNT_MAX + 1) { // wait 641ms UpdateAllWaveHeader(); } if (iNoDataCount >= 300) { // 3s mAudioDumpMutex.lock(); mAudioDumpSleepTime = -1; mAudioDumpMutex.unlock(); //ALOGD("AudioDumpThread, wait for new data dump\n"); pthread_mutex_lock(&AudioDataNotifyMutex); pthread_cond_wait(&AudioDataNotifyEvent, &AudioDataNotifyMutex); pthread_mutex_unlock(&AudioDataNotifyMutex); //ALOGD("AudioDumpThread, PCM data dump again\n"); } else { mAudioDumpMutex.lock(); mAudioDumpSleepTime = 10; mAudioDumpMutex.unlock(); usleep(mAudioDumpSleepTime * 1000); } } else { iNoDataCount = 0; mAudioDumpMutex.lock(); mAudioDumpSleepTime = 2; mAudioDumpMutex.unlock(); usleep(mAudioDumpSleepTime * 1000); } if (++iLoopCount >= 4 * AUDIO_DUMP_BUFFER_COUNT_MAX) { bFlush = true; iLoopCount = 0; } } ALOGD("AudioDumpThread exit hAudioDumpThread=%ld", hAudioDumpThread); hAudioDumpThread = 0; pthread_exit(NULL); return 0; } // Audio Dump Thread E //class AudioDump void AudioDump::dump(const char * filepath, void * buffer, int count) { { // mAudioDumpKeyMutex region Mutex::Autolock _l(mAudioDumpKeyMutex); ssize_t index = mAudioDumpKeyEnableVector.indexOfKey(PROP_AUDIO_DUMP_LOG); if (index < 0) { // new add int bflag = (int)property_get_bool(af_dump_log, false); mAudioDumpKeyEnableVector.add(PROP_AUDIO_DUMP_LOG, bflag); } } int ret = checkPath(filepath); if (ret < 0) { // ALOGE("dump fail!!!"); } else { FILE * fp= fopen(filepath, "ab+"); if (fp != NULL) { fwrite_s(buffer, 1, count, fp); if (fclose(fp) != 0) { ALOGE("%s(): Couldn't close dump file", __FUNCTION__); } if (mAudioDumpKeyEnableVector.valueFor(PROP_AUDIO_DUMP_LOG) > 0) { ALOGD("dump() %s(%d)", filepath, count); // For dump analysis } else { ALOGV("dump() %s(%d)", filepath, count); // For dump analysis } } else { //ALOGE("dump %s fail",property); } } } void AudioDump::threadDump(const char * path, void * buffer, int count, audio_format_t format, uint32_t sampleRate, uint32_t channelCount) { { // mAudioDumpKeyMutex region Mutex::Autolock _l(mAudioDumpKeyMutex); ssize_t index = mAudioDumpKeyEnableVector.indexOfKey(PROP_AUDIO_DUMP_LOG); if (index < 0) { // new add int bflag = (int)property_get_bool(af_dump_log, false); mAudioDumpKeyEnableVector.add(PROP_AUDIO_DUMP_LOG, bflag); } } { Mutex::Autolock _l(mAudioDumpMutex); if (hAudioDumpThread == 0) { char value[PROPERTY_VALUE_MAX] = {}; property_get(AUDIO_DUMP_FILE_DELAY_TIME_KEY, value, "2"); mAudioDumpFileIoDelayTime = atoi(value); //create PCM data dump thread here int ret = pthread_create(&hAudioDumpThread, NULL, AudioDumpThread, NULL); if (ret != 0) { // ALOGE("hAudioDumpThread create fail!!!"); } else { // ALOGD("hAudioDumpThread=%p created", hAudioDumpThread); } /*struct sched_param sched; sched.sched_priority = 0; ret = pthread_setschedparam( hAudioDumpThread, SCHED_OTHER, &sched ); if (ret != 0) { ALOGE("pthread_setschedparam() fail!!!"); }*/ ret = pthread_cond_init(&AudioDataNotifyEvent, NULL); if (ret != 0) { ALOGE("AudioDataNotifyEvent create fail!!!"); } ret = pthread_mutex_init(&AudioDataNotifyMutex, NULL); if (ret != 0) { ALOGE("AudioDataNotifyMutex create fail!!!"); } } } pushBufferInfo(path, buffer, count, format, sampleRate, channelCount); { Mutex::Autolock _l(mAudioDumpKeyMutex); if (mAudioDumpKeyEnableVector.valueFor(PROP_AUDIO_DUMP_LOG) > 0) { ALOGD("threadDump() %s(%d)", path, count); // For dump analysis } else { ALOGV("threadDump() %s(%d)", path, count); // For dump analysis } } } bool AudioDump::getProperty(AudioDump::PROP_AUDIO_DUMP key) { Mutex::Autolock _l(mAudioDumpKeyMutex); // get property from cache ssize_t index = mAudioDumpKeyEnableVector.indexOfKey(key); if (index < 0) { // new add int bflag = (int)property_get_bool(AudioDump::audioDumpPropertyStr[key], false); mAudioDumpKeyEnableVector.add(key, bflag); return bflag; } return mAudioDumpKeyEnableVector[index]; } int AudioDump::checkPath(const char * path) { char tmp[PATH_MAX]; unsigned i = 0; while (*path) { tmp[i] = *path; if (*path == '/' && i) { tmp[i] = '\0'; Mutex::Autolock _l(mAudioDumpFileMutex); if (access(tmp, F_OK) != 0) { if (mkdir(tmp, 0770) == -1) { // ALOGE("mkdir error! %s",(char*)strerror(errno)); return -1; } } tmp[i] = '/'; } i++; path++; } return 0; } unsigned long AudioDump::getDumpSize(const char * path) { unsigned long dump_size_in_queue = 0; Vector *pQueueBufferVector = NULL; mAudioDumpMutex.lock(); ssize_t index = mAudioDumpQueueVector.indexOfKey(String8(path)); if (index >= 0) { pQueueBufferVector = mAudioDumpQueueVector.valueAt(index); int queueSize = (*pQueueBufferVector).size(); for (int i = 0; i < queueSize; i++) { AudioDumpBuffer *newQueueBuffer = (*pQueueBufferVector)[i]; dump_size_in_queue += newQueueBuffer->fileInfo.size; } } mAudioDumpMutex.unlock(); struct stat dump_stat; String8 wavpath = String8::format("%s.wav", path); if (stat(wavpath, &dump_stat) == 0) { return dump_stat.st_size > AUDIO_DUMP_FILE_WAV_HEADER_SIZE ? (dump_size_in_queue + dump_stat.st_size - AUDIO_DUMP_FILE_WAV_HEADER_SIZE) : dump_size_in_queue; } return dump_size_in_queue; } void AudioDump::updateKeys(int key) { Mutex::Autolock _l(mAudioDumpKeyMutex); if (key < 0 || key >= AudioDump::PROP_AUDIO_DUMP_MAXNUM) { mAudioDumpKeyEnableVector.clear(); } else { mAudioDumpKeyEnableVector.removeItem((AudioDump::PROP_AUDIO_DUMP)key); } } void AudioDump::pushBufferInfo(const char * path, void * buffer, int count, audio_format_t format, uint32_t sampleRate, uint32_t channelCount) { if (buffer != NULL && count > 0) { AudioDumpBuffer *newQueueBuffer = new AudioDumpBuffer(); newQueueBuffer->pBufBase = (char*)malloc(count); if (newQueueBuffer->pBufBase != NULL) { memcpy(newQueueBuffer->pBufBase, buffer, count); } else { //error handle? ALOGE("%s(): Error! Not enough memory for audio dump buffer size=%d", __FUNCTION__, count); } newQueueBuffer->fileInfo.format = format; newQueueBuffer->fileInfo.sampleRate = sampleRate; newQueueBuffer->fileInfo.channelCount = channelCount; newQueueBuffer->fileInfo.size = count; Vector *pQueueBufferVector = NULL; mAudioDumpMutex.lock(); ssize_t index = mAudioDumpQueueVector.indexOfKey(String8(path)); if (index < 0) { // new add pQueueBufferVector = new Vector; mAudioDumpQueueVector.add(String8(path), pQueueBufferVector); ALOGD("new threadDump() %s.wav(%d)", path, count); // For dump analysis } else { pQueueBufferVector = mAudioDumpQueueVector.valueAt(index); } if (pQueueBufferVector) { pQueueBufferVector->add(newQueueBuffer); } bool needWakeup = (mAudioDumpSleepTime == -1); mAudioDumpMutex.unlock(); if (needWakeup) { //need to send event pthread_mutex_lock(&AudioDataNotifyMutex); pthread_cond_signal(&AudioDataNotifyEvent); pthread_mutex_unlock(&AudioDataNotifyMutex); } } } void FeatureOption::getValues() { // FO values are cached in the system parser. bool foValue_MTK_BESLOUDNESS_RUN_WITH_HAL = appIsFeatureOptionEnabled(foName_MTK_BESLOUDNESS_RUN_WITH_HAL); bool foValue_MTK_BESLOUDNESS_SUPPORT = appIsFeatureOptionEnabled(foName_MTK_BESLOUDNESS_SUPPORT); const char * foValue_MTK_USB_PHONECALL = appGetFeatureOptionValue(foName_MTK_USB_PHONECALL); int foValue_MTK_AUDIO_NUMBER_OF_SPEAKER = atoi(appGetFeatureOptionValue(foName_MTK_AUDIO_NUMBER_OF_SPEAKER)); bool foValue_MTK_TTY_SUPPORT = appIsFeatureOptionEnabled(foName_MTK_TTY_SUPPORT); bool foValue_MTK_HIFIAUDIO_SUPPORT = appIsFeatureOptionEnabled(foName_MTK_HIFIAUDIO_SUPPORT); bool foValue_MTK_AUDIO_HIERARCHICAL_PARAM_SUPPORT = appIsFeatureOptionEnabled(foName_MTK_AUDIO_HIERARCHICAL_PARAM_SUPPORT); bool foValue_MTK_AURISYS_FRAMEWORK_SUPPORT = appIsFeatureOptionEnabled(foName_MTK_AURISYS_FRAMEWORK_SUPPORT); bool foValue_MTK_BT_HEARING_AID_SUPPORT = appIsFeatureOptionEnabled(foName_MTK_BT_HEARING_AID_SUPPORT); bool foValue_MTK_BT_HEARING_AID_USING_AOSP_FMW = appIsFeatureOptionEnabled(foName_MTK_BT_HEARING_AID_USING_AOSP_FMW); bool foValue_MTK_AUDIO_MTK_TC10_FEATURE = appIsFeatureOptionEnabled(foName_MTK_TC10_FEATURE); bool foValue_MTK_AUDIO_MTK_TC10_IN_HOUSE = appIsFeatureOptionEnabled(foName_MTK_TC10_IN_HOUSE); bool foValue_MTK_AUDIO_A2DP_LATENCY_IMPROVE = appIsFeatureOptionEnabled(foName_MTK_AUDIO_A2DP_LATENCY_IMPROVE); char bluetooth_le_mode[PROPERTY_VALUE_MAX] = { 0 }; property_get("persist.vendor.bluetooth.leaudio_mode", bluetooth_le_mode,MTK_LEAUDIO_MODE_OFF); bool foValue_MTK_BLE_PHONECALL = (strncmp(bluetooth_le_mode, MTK_LEAUDIO_MODE_CGUMS, strlen(MTK_LEAUDIO_MODE_CGUMS)) == 0); const char * foValue_MTK_MUSIC_ROUTE_TO_BTSCO_DURING_BTSCO_ON = appGetFeatureOptionValue(foName_MTK_MUSIC_ROUTE_TO_BTSCO_DURING_BTSCO_ON); MTK_AUDIOMIXER_ENABLE_DRC = !foValue_MTK_BESLOUDNESS_RUN_WITH_HAL && foValue_MTK_BESLOUDNESS_SUPPORT; ALOGD("MTK_AUDIOMIXER_ENABLE_DRC: %d", MTK_AUDIOMIXER_ENABLE_DRC); MTK_ENABLE_STEREO_SPEAKER = (foValue_MTK_AUDIO_NUMBER_OF_SPEAKER >= 2) ? 1 : 0; ALOGD("MTK_ENABLE_STEREO_SPEAKER: %d", MTK_ENABLE_STEREO_SPEAKER); MTK_USB_PHONECALL = foValue_MTK_USB_PHONECALL != NULL ? strcmp(foValue_MTK_USB_PHONECALL, "no"): true; ALOGD("foValue_MTK_USB_PHONECALL %s, MTK_USB_PHONECALL: %d", foValue_MTK_USB_PHONECALL, MTK_USB_PHONECALL); MTK_TTY_SUPPORT = foValue_MTK_TTY_SUPPORT; ALOGD("MTK_TTY_SUPPORT %d", MTK_TTY_SUPPORT); MTK_HIFIAUDIO_SUPPORT = foValue_MTK_HIFIAUDIO_SUPPORT; ALOGD("MTK_HIFIAUDIO_SUPPORT: %d", MTK_HIFIAUDIO_SUPPORT); MTK_BESLOUDNESS_ENABLE = foValue_MTK_BESLOUDNESS_SUPPORT; ALOGD("MTK_BESLOUDNESS_ENABLE : %d", MTK_BESLOUDNESS_ENABLE); MTK_AUDIO_GAIN_TABLE = foValue_MTK_AUDIO_HIERARCHICAL_PARAM_SUPPORT; MTK_AUDIO_GAIN_NVRAM = !MTK_AUDIO_GAIN_TABLE; ALOGD("MTK_AUDIO_GAIN_TABLE: %d", MTK_AUDIO_GAIN_TABLE); ALOGD("MTK_FM_ENABLE : %d", MTK_FM_ENABLE); MTK_AURISYS_FRAMEWORK_SUPPORT = foValue_MTK_AURISYS_FRAMEWORK_SUPPORT; ALOGD("MTK_AURISYS_FRAMEWORK_SUPPORT : %d", MTK_AURISYS_FRAMEWORK_SUPPORT); MTK_BT_HEARING_AID_SUPPORT = foValue_MTK_BT_HEARING_AID_SUPPORT; ALOGD("MTK_BT_HEARING_AID_SUPPORT : %d", MTK_BT_HEARING_AID_SUPPORT); MTK_BT_HEARING_AID_USING_AOSP_FMW = foValue_MTK_BT_HEARING_AID_USING_AOSP_FMW; ALOGD("MTK_BT_HEARING_AID_USING_AOSP_FMW : %d", MTK_BT_HEARING_AID_USING_AOSP_FMW); MTK_TC10_FEATURE = foValue_MTK_AUDIO_MTK_TC10_FEATURE; ALOGD("MTK_TC10_FEATURE : %d", MTK_TC10_FEATURE); MTK_TC10_IN_HOUSE = foValue_MTK_AUDIO_MTK_TC10_IN_HOUSE; ALOGD("MTK_TC10_IN_HOUSE : %d", MTK_TC10_IN_HOUSE); MTK_AUDIO_A2DP_LATENCY_IMPROVE = foValue_MTK_AUDIO_A2DP_LATENCY_IMPROVE; ALOGD("MTK_AUDIO_A2DP_LATENCY_IMPROVE : %d", MTK_AUDIO_A2DP_LATENCY_IMPROVE); MTK_MUSIC_ROUTE_TO_BTSCO_DURING_BTSCO_ON = foValue_MTK_MUSIC_ROUTE_TO_BTSCO_DURING_BTSCO_ON != NULL ? strcmp(foValue_MTK_MUSIC_ROUTE_TO_BTSCO_DURING_BTSCO_ON, "no"): true; ALOGD("foValue_MTK_MUSIC_ROUTE_TO_BTSCO_DURING_BTSCO_ON %s, MTK_MUSIC_ROUTE_TO_BTSCO_DURING_BTSCO_ON : %d", foValue_MTK_MUSIC_ROUTE_TO_BTSCO_DURING_BTSCO_ON, MTK_MUSIC_ROUTE_TO_BTSCO_DURING_BTSCO_ON); MTK_BLE_PHONECALL = foValue_MTK_BLE_PHONECALL; ALOGD("MTK_BLE_PHONECALL : %d", MTK_BLE_PHONECALL); } void AudioUtilThread::exit() { { AutoMutex lock(mLock); requestExit(); mWaitWorkCV.broadcast(); } requestExitAndWait(); } bool AudioUtilThread::threadLoop() { while (!exitPending()) { Mutex::Autolock _l(mLock); // mVector; { Mutex::Autolock _l(mBatteryStateVectorLock); for (int i = 0; i < mBatteryStateVector.size(); i++) { mVector.push_back(mBatteryStateVector[i]); } mBatteryStateVector.clear(); } for (int i = 0; i < mVector.size(); i++) { //ALOGD("%s+, event %d, uid %d", __func__, (int)mVector[i].event, (int)mVector[i].uid); if (mVector[i].event == BATTERY_NOTE_START_AUDIO) { BatteryNotifier::getInstance().noteStartAudio(mVector[i].uid); } else if (mVector[i].event == BATTERY_NOTE_STOP_AUDIO) { BatteryNotifier::getInstance().noteStopAudio(mVector[i].uid); } } // BatteryNotifier> mWaitWorkCV.wait(mLock); } ALOGV("AudioUtilThread %p exiting", this); return false; } void AudioUtilThread::BatteryStateRegister(BATTERY_EVENT event, uint32_t uid) { //ALOGD("%s, event %d, uid %d", __func__, (int)event, (int)uid); BatteryNote note; note.event = event; note.uid = uid; Mutex::Autolock _l(mBatteryStateVectorLock); mBatteryStateVector.add(note); } void AudioUtilThread::BatteryStateNotify() { //Mutex::Autolock _l(mBatteryStateVectorLock); // no need!! if (mBatteryStateVector.size() > 0) { mWaitWorkCV.signal(); } } }