/* * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. * All rights reserved. * * File : soundControl.cpp * Description : soundControl for android * History : * */ #include #include #include #include #include #include "soundControl.h" #include "cdx_log.h" #include #include #include #include #include "outputCtrl.h" using namespace android; #define SAVE_PCM (0) #if SAVE_PCM const char* binpath = "/data/camera/test.bin"; #endif enum ESOUNDSTATUS { SOUND_STATUS_STOPPED = 0, SOUND_STATUS_STARTED, SOUND_STATUS_PAUSED }; typedef struct AudioDirectOutPutSink { sp pAudioTrackHandle; audio_output_flags_t flag; audio_format_t format; audio_channel_mask_t chanmask; unsigned int samplerate; }AudioDirectOutPutSink; typedef struct SoundCtrlContext { SoundCtrl base; MediaPlayerBase::AudioSink* pAudioSink; sp pAudioTrack;//* new an audio track if pAudioSink not set. unsigned int nSampleRate; unsigned int nChannelNum; unsigned int nBitPerSample; unsigned int needDirectOutPut; enum AUIDO_RAW_DATA_TYPE data_type; AudioDirectOutPutSink directSink; unsigned int nRoutine; #if (CONF_ANDROID_MAJOR_VER > 5) AudioPlaybackRate nPlayRate; #endif int64_t nDataSizePlayed; int64_t nFramePosOffset; unsigned int nFrameSize; unsigned int nLastFramePos; enum ESOUNDSTATUS eStatus; pthread_mutex_t mutex; float volume; #if SAVE_PCM FILE* testpcm; #endif #if defined(CONF_SC_WRITE_ASYNC_MODE) size_t fillBuffer(void *data, size_t size); void *pBufAudioSink; size_t nSizeAudioSink; int bReachedEOS; pthread_cond_t cbCond; pthread_mutex_t cbMutex; #endif }SoundCtrlContext; static int SoundDeviceStop_l(SoundCtrlContext* sc); static void SoundClearDircectOutSink_l(SoundCtrlContext* sc) { if(sc->directSink.pAudioTrackHandle != NULL) { sc->directSink.pAudioTrackHandle->stop(); sc->directSink.pAudioTrackHandle.clear(); sc->directSink.pAudioTrackHandle = NULL; sc->directSink.flag = AUDIO_OUTPUT_FLAG_NONE; sc->directSink.format = AUDIO_FORMAT_DEFAULT; sc->directSink.chanmask = AUDIO_CHANNEL_NONE; sc->directSink.samplerate = 0; } } static void __SdDestroy(SoundCtrl* s) { SoundCtrlContext* sc; logd(": destroy"); sc = (SoundCtrlContext*)s; pthread_mutex_lock(&sc->mutex); if(sc->eStatus != SOUND_STATUS_STOPPED) SoundDeviceStop_l(sc); SoundClearDircectOutSink_l(sc); pthread_mutex_unlock(&sc->mutex); pthread_mutex_destroy(&sc->mutex); #if defined(CONF_SC_WRITE_ASYNC_MODE) if(sc->pAudioSink != NULL) { pthread_mutex_destroy(&sc->cbMutex); pthread_cond_destroy(&sc->cbCond); } #endif #if SAVE_PCM fclose(sc->testpcm); sc->testpcm = NULL; #endif free(sc); return; } static void __SdSetFormat(SoundCtrl* s, CdxPlaybkCfg* cfg) { SoundCtrlContext* sc; sc = (SoundCtrlContext*)s; pthread_mutex_lock(&sc->mutex); if(sc->eStatus != SOUND_STATUS_STOPPED) { loge("Sound device not int stop status, can not set audio params."); abort(); } if(cfg == NULL) { loge("invalid operation in __SdSetFormat"); pthread_mutex_unlock(&sc->mutex); return; } else { sc->nBitPerSample = cfg->nBitpersample; sc->nSampleRate = cfg->nSamplerate; sc->nChannelNum = cfg->nChannels; sc->needDirectOutPut = cfg->nNeedDirect; sc->data_type = cfg->nDataType; sc->nRoutine = cfg->nRoutine; pthread_mutex_unlock(&sc->mutex); return; } } #if defined(CONF_SC_WRITE_ASYNC_MODE) size_t SoundCtrlContext::fillBuffer(void *data, size_t size) { size_t doneSize = 0; if(bReachedEOS == 1) { return 0; } pthread_mutex_lock(&cbMutex); pBufAudioSink = data; nSizeAudioSink = size; while(nSizeAudioSink > 0) { if(bReachedEOS == 1) break; pthread_cond_wait(&cbCond, &cbMutex); } doneSize = size - nSizeAudioSink; if(doneSize == size) { pBufAudioSink = NULL; } pthread_mutex_unlock(&cbMutex); return doneSize; } // static size_t SoundAudioSinkCallback( MediaPlayerBase::AudioSink * /* audioSink */, void *buffer, size_t size, void *cookie, MediaPlayerBase::AudioSink::cb_event_t event) { SoundCtrlContext* sc = (SoundCtrlContext *)cookie; switch(event) { case MediaPlayerBase::AudioSink::CB_EVENT_FILL_BUFFER: return sc->fillBuffer(buffer, size); case MediaPlayerBase::AudioSink::CB_EVENT_STREAM_END: ALOGV("SoundAudioSinkCallback: stream end"); sc->bReachedEOS = 1; //sc->notifyAudioEOS(); break; case MediaPlayerBase::AudioSink::CB_EVENT_TEAR_DOWN: ALOGV("SoundAudioSinkCallback: Tear down event"); //sc->mObserver->postAudioTearDown(); break; } return 0; } #endif static int __SdStart(SoundCtrl* s) { SoundCtrlContext* sc; status_t err; unsigned int nFramePos; audio_output_flags_t flag = AUDIO_OUTPUT_FLAG_NONE; audio_format_t format = AUDIO_FORMAT_PCM_16_BIT; audio_channel_mask_t chanmask = AUDIO_CHANNEL_OUT_STEREO; sc = (SoundCtrlContext*)s; logd(": start"); pthread_mutex_lock(&sc->mutex); if(sc->eStatus == SOUND_STATUS_STARTED) { logw("Sound device already started."); pthread_mutex_unlock(&sc->mutex); return -1; } if(sc->eStatus == SOUND_STATUS_STOPPED) { if(sc->needDirectOutPut) { flag = AUDIO_OUTPUT_FLAG_DIRECT; switch(sc->data_type) { case AUDIO_RAW_DATA_AC3: logd("cedarx passthrough data ac3"); format = AUDIO_FORMAT_AC3; break; case AUDIO_RAW_DATA_DTS: logd("cedarx passthrough data dts"); format = AUDIO_FORMAT_DTS; break; case AUDIO_RAW_DATA_DOLBY_DIGITAL_PLUS: logd("cedarx passthrough data ddp"); format = AUDIO_FORMAT_E_AC3; break; default: break; } if(sc->data_type == AUDIO_RAW_DATA_PCM) { format = AUDIO_FORMAT_PCM_16_BIT; switch(sc->nBitPerSample) { case 32: format = AUDIO_FORMAT_PCM_32_BIT; break; default: break; } switch(sc->nChannelNum) { case 6: chanmask = AUDIO_CHANNEL_OUT_5POINT1; break; case 8: chanmask = AUDIO_CHANNEL_OUT_7POINT1; break; default: break; } } } if(sc->pAudioSink != NULL && !sc->needDirectOutPut) { if(!sc->pAudioSink->ready()){ logd("Cedarx use AudioSink open spr : %d, ch : %d, fmt : %p", sc->nSampleRate, sc->nChannelNum, format); #if defined(CONF_SC_WRITE_ASYNC_MODE) err = sc->pAudioSink->open(sc->nSampleRate, sc->nChannelNum, CHANNEL_MASK_USE_CHANNEL_ORDER, format, DEFAULT_AUDIOSINK_BUFFERCOUNT*5, &SoundAudioSinkCallback, sc, flag); #else //sync mode err = sc->pAudioSink->open(sc->nSampleRate, sc->nChannelNum, CHANNEL_MASK_USE_CHANNEL_ORDER, format, DEFAULT_AUDIOSINK_BUFFERCOUNT*5, NULL, //* no callback mode. NULL, flag); #endif if(err != OK) { pthread_mutex_unlock(&sc->mutex); return -1; } } else{ logd("Audiosink has opened"); } unsigned int tmp = 0; sc->pAudioSink->getPosition(&tmp); if(tmp != 0) { sc->pAudioSink->pause(); sc->pAudioSink->flush(); } sc->nFrameSize = sc->pAudioSink->frameSize(); SoundClearDircectOutSink_l(sc); } else if(sc->needDirectOutPut) { do{ logd("CedarX request Direct output to transfer iec61937 stream(or muti ch pcm)"); if(sc->pAudioTrack == NULL) { if(sc->directSink.pAudioTrackHandle != NULL) { if(sc->directSink.chanmask == chanmask && sc->directSink.flag == flag && sc->directSink.format == format && sc->directSink.samplerate == sc->nSampleRate) { logd("Direct out put reuse audiotrack from recycle"); sc->pAudioTrack = sc->directSink.pAudioTrackHandle; break; } else { SoundClearDircectOutSink_l(sc); } } logd("Direct out put new a Audiotrack"); sc->pAudioTrack = new AudioTrack(); if(sc->pAudioTrack == NULL) { loge("create audio track fail."); pthread_mutex_unlock(&sc->mutex); return -1; } sc->pAudioTrack->set(AUDIO_STREAM_MUSIC, sc->nSampleRate, format, chanmask, DEFAULT_AUDIOSINK_BUFFERCOUNT*5, flag); if(sc->directSink.pAudioTrackHandle != NULL) loge("damn it! maybe memory leak!!!"); sc->directSink.flag = flag; sc->directSink.format = format; sc->directSink.chanmask = chanmask; sc->directSink.samplerate = sc->nSampleRate; sc->directSink.pAudioTrackHandle = sc->pAudioTrack; } else { logd("Direct out put reuse audiotrack from self"); } } while(0); if(sc->pAudioTrack->initCheck() != OK) { loge("audio track initCheck() return fail."); sc->pAudioTrack.clear(); sc->pAudioTrack = NULL; pthread_mutex_unlock(&sc->mutex); return -1; } sc->nFrameSize = sc->pAudioTrack->frameSize(); logd("framesize infomation : nFrameSize : %d", sc->nFrameSize); #if (CONF_ANDROID_MAJOR_VER > 5) if (sc->nPlayRate.mSpeed != 1.0f) sc->pAudioTrack->setPlaybackRate(sc->nPlayRate); #endif } else { sc->pAudioTrack = new AudioTrack(); if(sc->pAudioTrack == NULL) { loge("create audio track fail."); pthread_mutex_unlock(&sc->mutex); return -1; } sc->pAudioTrack->set(AUDIO_STREAM_DEFAULT, sc->nSampleRate, format, (sc->nChannelNum == 2) ? AUDIO_CHANNEL_OUT_STEREO : AUDIO_CHANNEL_OUT_MONO,20*1024); if(sc->pAudioTrack->initCheck() != OK) { loge("audio track initCheck() return fail."); sc->pAudioTrack.clear(); sc->pAudioTrack = NULL; pthread_mutex_unlock(&sc->mutex); return -1; } sc->nFrameSize = sc->pAudioTrack->frameSize(); #if (CONF_ANDROID_MAJOR_VER > 5) if (sc->nPlayRate.mSpeed != 1.0f) sc->pAudioTrack->setPlaybackRate(sc->nPlayRate); #endif } sc->nDataSizePlayed = 0; sc->nFramePosOffset = 0; sc->nLastFramePos = 0; } if(sc->pAudioSink != NULL && !sc->needDirectOutPut) sc->pAudioSink->start(); else sc->pAudioTrack->start(); sc->eStatus = SOUND_STATUS_STARTED; pthread_mutex_unlock(&sc->mutex); return 0; } static int __SdStop(SoundCtrl* s) { int ret; SoundCtrlContext* sc; logd(": stop"); sc = (SoundCtrlContext*)s; pthread_mutex_lock(&sc->mutex); ret = SoundDeviceStop_l(sc); pthread_mutex_unlock(&sc->mutex); return ret; } static int SoundDeviceStop_l(SoundCtrlContext* sc) { if(sc->eStatus == SOUND_STATUS_STOPPED) { logw("Sound device already stopped."); return 0; } if(sc->pAudioSink != NULL && !sc->needDirectOutPut) { #if defined(CONF_SC_WRITE_ASYNC_MODE) pthread_mutex_lock(&sc->cbMutex); sc->bReachedEOS = 1; pthread_cond_signal(&sc->cbCond); pthread_mutex_unlock(&sc->cbMutex); #endif sc->pAudioSink->pause(); sc->pAudioSink->flush(); sc->pAudioSink->stop(); sc->pAudioSink->close(); } else { if (sc->pAudioTrack.get() != NULL) { sc->pAudioTrack->pause(); sc->pAudioTrack->flush(); sc->pAudioTrack->stop(); sc->pAudioTrack.clear(); sc->pAudioTrack = NULL; } } sc->nDataSizePlayed = 0; sc->nFramePosOffset = 0; sc->nLastFramePos = 0; sc->nFrameSize = 0; sc->eStatus = SOUND_STATUS_STOPPED; return 0; } static int __SdPause(SoundCtrl* s) { SoundCtrlContext* sc; sc = (SoundCtrlContext*)s; logd(": pause"); pthread_mutex_lock(&sc->mutex); if(sc->eStatus != SOUND_STATUS_STARTED) { logw("Invalid pause operation, sound device not in start status."); pthread_mutex_unlock(&sc->mutex); return -1; } if(sc->pAudioSink != NULL && !sc->needDirectOutPut) sc->pAudioSink->pause(); else { if (sc->pAudioTrack.get() != NULL) { sc->pAudioTrack->pause(); } } sc->eStatus = SOUND_STATUS_PAUSED; pthread_mutex_unlock(&sc->mutex); return 0; } static int __SdWrite(SoundCtrl* s, void* pData, int nDataSize) { int nWritten = 0; SoundCtrlContext* sc; sc = (SoundCtrlContext*)s; pthread_mutex_lock(&sc->mutex); if(sc->eStatus == SOUND_STATUS_STOPPED || sc->eStatus == SOUND_STATUS_PAUSED) { pthread_mutex_unlock(&sc->mutex); return 0; } #if SAVE_PCM fwrite(pData, 1, nDataSize,sc->testpcm); #endif #if defined(CONF_SC_WRITE_ASYNC_MODE) if(sc->pAudioSink != NULL && !sc->needDirectOutPut) { //memcpy to the buf from audioSink pthread_mutex_lock(&sc->cbMutex); if(sc->nSizeAudioSink > 0 && sc->pBufAudioSink != NULL) { nWritten = sc->nSizeAudioSink < (size_t)nDataSize ? sc->nSizeAudioSink : nDataSize; memcpy(sc->pBufAudioSink, pData, nWritten); sc->nSizeAudioSink -= (size_t)nWritten; sc->pBufAudioSink = (char *)sc->pBufAudioSink + nWritten; } else { nWritten = -1;//buf is not ready } pthread_cond_signal(&sc->cbCond); pthread_mutex_unlock(&sc->cbMutex); } #else if(sc->pAudioSink != NULL && !sc->needDirectOutPut) nWritten = sc->pAudioSink->write(pData, nDataSize); #endif else { if (sc->pAudioTrack.get() != NULL) { nWritten = sc->pAudioTrack->write(pData, nDataSize); } } if(nWritten < 0) nWritten = 0; else sc->nDataSizePlayed += nWritten; pthread_mutex_unlock(&sc->mutex); return nWritten; } static int SoundDeviceReset_l(SoundCtrlContext* sc) { if(sc->eStatus == SOUND_STATUS_STOPPED) { logd("Sound device already in Idle"); return -1; } if(sc->pAudioSink != NULL && !sc->needDirectOutPut) { #if defined(CONF_SC_WRITE_ASYNC_MODE) pthread_mutex_lock(&sc->cbMutex); pthread_cond_signal(&sc->cbCond); pthread_mutex_unlock(&sc->cbMutex); #endif sc->pAudioSink->pause(); sc->pAudioSink->flush(); sc->pAudioSink->stop(); } else { if (sc->pAudioTrack.get() != NULL) { sc->pAudioTrack->pause(); sc->pAudioTrack->flush(); sc->pAudioTrack->stop(); } } sc->nDataSizePlayed = 0; sc->nFramePosOffset = 0; sc->nLastFramePos = 0; sc->eStatus = SOUND_STATUS_STOPPED; return 0; } //* called at player seek operation. static int __SdReset(SoundCtrl* s) { SoundCtrlContext* sc; int ret = 0; logd(": reset"); sc = (SoundCtrlContext*)s; pthread_mutex_lock(&sc->mutex); ret = SoundDeviceReset_l(sc); pthread_mutex_unlock(&sc->mutex); return ret; } static int __SdGetCachedTime(SoundCtrl* s) { unsigned int nFramePos; int64_t nCachedFrames; int64_t nCachedTimeUs; SoundCtrlContext* sc; sc = (SoundCtrlContext*)s; pthread_mutex_lock(&sc->mutex); if(sc->eStatus == SOUND_STATUS_STOPPED) { pthread_mutex_unlock(&sc->mutex); return 0; } if(sc->pAudioSink != NULL && !sc->needDirectOutPut) sc->pAudioSink->getPosition(&nFramePos); else { if (sc->pAudioTrack.get() != NULL) { sc->pAudioTrack->getPosition(&nFramePos); } } if(sc->nFrameSize == 0) { loge("nFrameSize == 0."); abort(); } if(nFramePos < sc->nLastFramePos) sc->nFramePosOffset += 0x100000000; nCachedFrames = sc->nDataSizePlayed/sc->nFrameSize - nFramePos - sc->nFramePosOffset; nCachedTimeUs = nCachedFrames*1000000/sc->nSampleRate; logv("nDataSizePlayed = %lld, nFrameSize = %d, nFramePos = %u, \ nLastFramePos = %u, nFramePosOffset = %lld", sc->nDataSizePlayed, sc->nFrameSize, nFramePos, sc->nLastFramePos, sc->nFramePosOffset); logv("nCachedFrames = %lld, nCachedTimeUs = %lld, nSampleRate = %d", nCachedFrames, nCachedTimeUs, sc->nSampleRate); sc->nLastFramePos = nFramePos; pthread_mutex_unlock(&sc->mutex); return (int)(nCachedTimeUs); } static int __SdGetFrameCount(SoundCtrl* s) { SoundCtrlContext* sc = (SoundCtrlContext*)s; int framecount = 0; if (sc->pAudioSink != NULL && !sc->needDirectOutPut) { framecount = sc->pAudioSink->frameCount(); } else { if (sc->pAudioTrack.get() != NULL) { framecount = sc->pAudioTrack->frameCount(); } } logv("framecount = %d",framecount); return framecount; } #if (CONF_ANDROID_MAJOR_VER > 5) int _SetPlaybackRate(SoundCtrl* s,const XAudioPlaybackRate *rate) { SoundCtrlContext* sc = (SoundCtrlContext*)s; int ret =0; sc->nPlayRate.mSpeed = rate->mSpeed; sc->nPlayRate.mPitch = rate->mPitch; sc->nPlayRate.mStretchMode = (AudioTimestretchStretchMode)rate->mStretchMode; sc->nPlayRate.mFallbackMode = (AudioTimestretchFallbackMode)rate->mFallbackMode; ALOGD("rate.mSpeed %f",sc->nPlayRate.mSpeed); ALOGD("rate.mPitch %f",sc->nPlayRate.mPitch); ALOGD("rate.mFallbackMode %d",sc->nPlayRate.mFallbackMode); ALOGD("rate.mStretchMode %d",sc->nPlayRate.mStretchMode); if (sc->pAudioSink != NULL && !sc->needDirectOutPut) { ret = sc->pAudioSink->setPlaybackRate(sc->nPlayRate); } else { if (sc->pAudioTrack == NULL) __SdStart(s); ret = sc->pAudioTrack->setPlaybackRate(sc->nPlayRate); } return ret; } #else int _SetPlaybackRate(SoundCtrl* s,const XAudioPlaybackRate *rate) { CDX_UNUSE(s); CDX_UNUSE(rate); return 0; } #endif static SoundControlOpsT mSoundControlOps = { .destroy = __SdDestroy, .setFormat = __SdSetFormat, .start = __SdStart, .stop = __SdStop, .pause = __SdPause, .write = __SdWrite, .reset = __SdReset, .getCachedTime = __SdGetCachedTime, .getFrameCount = __SdGetFrameCount, .setPlaybackRate = _SetPlaybackRate, }; SoundCtrl* SoundDeviceCreate(void* pAudioSink) { SoundCtrlContext* s; logd(": init"); s = (SoundCtrlContext*)malloc(sizeof(SoundCtrlContext)); if(s == NULL) { loge("malloc memory fail."); return NULL; } memset(s, 0, sizeof(SoundCtrlContext)); s->base.ops = &mSoundControlOps; s->pAudioSink = (MediaPlayerBase::AudioSink*)pAudioSink; s->pAudioTrack = NULL; #if (CONF_ANDROID_MAJOR_VER > 5) s->nPlayRate.mSpeed = 1.0f; #endif s->eStatus = SOUND_STATUS_STOPPED; #if SAVE_PCM s->testpcm = fopen(binpath,"wb"); if(!s->testpcm) { loge("open file failed, %s", strerror(errno)); } #endif pthread_mutex_init(&s->mutex, NULL); #if defined(CONF_SC_WRITE_ASYNC_MODE) if(s->pAudioSink != NULL) { pthread_mutex_init(&s->cbMutex, NULL); pthread_cond_init(&s->cbCond, NULL); } #endif return (SoundCtrl*)&s->base; }