SmartAudio/package/allwinner/liballwinner_tina/tinaplayer/tinasoundcontrol.cpp

476 lines
14 KiB
C++
Executable File

#define LOG_TAG "TinaSoundControl"
#include "tinasoundcontrol.h"
#include <pthread.h>
#include <sys/time.h>
#include <assert.h>
#include <allwinner/auGaincom.h>
#include "log.h"
namespace aw{
int BLOCK_MODE = 0;
int NON_BLOCK_MODE = 1;
static int openSoundDevice(SoundCtrlContext* sc,int mode);
static int closeSoundDevice(SoundCtrlContext* sc);
static int setSoundDeviceParams(SoundCtrlContext* sc);
static int openSoundDevice(SoundCtrlContext* sc ,int mode)
{
int ret = 0;
logd("openSoundDevice() in dmix style");
assert(sc);
if(!sc->alsa_handler){
//if((ret = snd_pcm_open(&sc->alsa_handler, "default",SND_PCM_STREAM_PLAYBACK ,mode))<0){
//if use dimix func,use the below parms open the sound card
if((ret = snd_pcm_open(&sc->alsa_handler, "plug:dmix",SND_PCM_STREAM_PLAYBACK ,mode))<0){
loge("open audio device failed:%s, errno = %d",strerror(errno),errno);
if(errno == 16){//the device is busy,sleep 2 second and try again
sleep(2);
//if((ret = snd_pcm_open(&sc->alsa_handler, "default",SND_PCM_STREAM_PLAYBACK ,mode))<0){
//if use dimix func,use the below parms open the sound card
if((ret = snd_pcm_open(&sc->alsa_handler, "plug:dmix",SND_PCM_STREAM_PLAYBACK ,mode))<0){
loge("second open audio device failed:%s, errno = %d",strerror(errno),errno);
}
}
}
}else{
logd("the audio device has been opened");
}
return ret;
}
static int closeSoundDevice(SoundCtrlContext* sc)
{
int ret = 0;
logd("closeSoundDevice()");
assert(sc);
if (sc->alsa_handler){
if ((ret = snd_pcm_close(sc->alsa_handler)) < 0)
{
loge("snd_pcm_close failed:%s",strerror(errno));
}
else
{
sc->alsa_handler = NULL;
logd("alsa-uninit: pcm closed");
}
}
return ret;
}
static int setSoundDeviceParams(SoundCtrlContext* sc)
{
int ret = 0;
logd("setSoundDeviceParams()");
assert(sc);
sc->bytes_per_sample = snd_pcm_format_physical_width(sc->alsa_format) / 8;
sc->bytes_per_sample *= sc->nChannelNum;
sc->alsa_fragcount = 8;
sc->chunk_size = 1024;
if ((ret = snd_pcm_hw_params_malloc(&sc->alsa_hwparams)) < 0)
{
loge("snd_pcm_hw_params_malloc failed:%s",strerror(errno));
return ret;
}
if ((ret = snd_pcm_hw_params_any(sc->alsa_handler, sc->alsa_hwparams)) < 0)
{
loge("snd_pcm_hw_params_any failed:%s",strerror(errno));
goto SET_PAR_ERR;
}
if ((ret = snd_pcm_hw_params_set_access(sc->alsa_handler, sc->alsa_hwparams,
SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
loge("snd_pcm_hw_params_set_access failed:%s",strerror(errno));
goto SET_PAR_ERR;
}
if ((ret = snd_pcm_hw_params_test_format(sc->alsa_handler, sc->alsa_hwparams,
sc->alsa_format)) < 0)
{
loge("snd_pcm_hw_params_test_format fail , MSGTR_AO_ALSA_FormatNotSupportedByHardware");
sc->alsa_format = SND_PCM_FORMAT_S16_LE;
}
if ((ret = snd_pcm_hw_params_set_format(sc->alsa_handler, sc->alsa_hwparams,
sc->alsa_format)) < 0)
{
loge("snd_pcm_hw_params_set_format failed:%s",strerror(errno));
goto SET_PAR_ERR;
}
if ((ret = snd_pcm_hw_params_set_channels_near(sc->alsa_handler,
sc->alsa_hwparams, &sc->nChannelNum)) < 0) {
loge("snd_pcm_hw_params_set_channels_near failed:%s",strerror(errno));
goto SET_PAR_ERR;
}
if ((ret = snd_pcm_hw_params_set_rate_near(sc->alsa_handler, sc->alsa_hwparams,
&sc->nSampleRate, NULL)) < 0) {
loge("snd_pcm_hw_params_set_rate_near failed:%s",strerror(errno));
goto SET_PAR_ERR;
}
if ((ret = snd_pcm_hw_params_set_period_size_near(sc->alsa_handler,
sc->alsa_hwparams, &sc->chunk_size, NULL)) < 0) {
loge("snd_pcm_hw_params_set_period_size_near fail , MSGTR_AO_ALSA_UnableToSetPeriodSize");
goto SET_PAR_ERR;
} else {
logd("alsa-init: chunksize set to %ld", sc->chunk_size);
}
if ((ret = snd_pcm_hw_params_set_periods_near(sc->alsa_handler,
sc->alsa_hwparams, (unsigned int*)&sc->alsa_fragcount, NULL)) < 0)
{
loge("snd_pcm_hw_params_set_periods_near fail , MSGTR_AO_ALSA_UnableToSetPeriods");
goto SET_PAR_ERR;
} else {
logd("alsa-init: fragcount=%d", sc->alsa_fragcount);
}
if ((ret = snd_pcm_hw_params(sc->alsa_handler, sc->alsa_hwparams)) < 0) {
loge("snd_pcm_hw_params failed:%s",strerror(errno));
goto SET_PAR_ERR;
}
sc->alsa_can_pause = snd_pcm_hw_params_can_pause(sc->alsa_hwparams);
logd("setSoundDeviceParams():sc->alsa_can_pause = %d",sc->alsa_can_pause);
SET_PAR_ERR:
snd_pcm_hw_params_free(sc->alsa_hwparams);
return ret;
}
SoundCtrl* TinaSoundDeviceInit(void* pAudioSink){
SoundCtrlContext* s;
s = (SoundCtrlContext*)malloc(sizeof(SoundCtrlContext));
logd("TinaSoundDeviceInit()");
if(s == NULL)
{
loge("malloc SoundCtrlContext fail.");
return NULL;
}
memset(s, 0, sizeof(SoundCtrlContext));
s->alsa_access_type = SND_PCM_ACCESS_RW_INTERLEAVED;
s->nSampleRate = 48000;
s->nChannelNum = 2;
s->alsa_format = SND_PCM_FORMAT_S16_LE;
s->alsa_can_pause = 0;
s->sound_status = STATUS_STOP;
s->mVolume = 0;
pthread_mutex_init(&s->mutex, NULL);
return (SoundCtrl*)s;
}
void TinaSoundDeviceRelease(SoundCtrl* s){
SoundCtrlContext* sc;
sc = (SoundCtrlContext*)s;
assert(sc);
pthread_mutex_lock(&sc->mutex);
logd("TinaSoundDeviceRelease()");
if(sc->sound_status != STATUS_STOP){
closeSoundDevice(sc);
}
pthread_mutex_unlock(&sc->mutex);
pthread_mutex_destroy(&sc->mutex);
free(sc);
sc = NULL;
}
void TinaSoundDeviceSetFormat(SoundCtrl* s, unsigned int nSampleRate, unsigned int nChannelNum){
SoundCtrlContext* sc;
sc = (SoundCtrlContext*)s;
assert(sc);
pthread_mutex_lock(&sc->mutex);
logd("TinaSoundDeviceSetFormat(),sc->sound_status == %d",sc->sound_status);
if(sc->sound_status == STATUS_STOP){
logd("TinaSoundDeviceSetFormat()");
sc->nSampleRate = nSampleRate;
sc->nChannelNum = nChannelNum;
sc->alsa_format = SND_PCM_FORMAT_S16_LE;
sc->bytes_per_sample = snd_pcm_format_physical_width(sc->alsa_format) / 8;
sc->bytes_per_sample *= nChannelNum;
logd("TinaSoundDeviceSetFormat()>>>sample_rate:%d,channel_num:%d,sc->bytes_per_sample:%d",
nSampleRate,nChannelNum,sc->bytes_per_sample);
}
pthread_mutex_unlock(&sc->mutex);
}
int TinaSoundDeviceStart(SoundCtrl* s){
SoundCtrlContext* sc;
sc = (SoundCtrlContext*)s;
assert(sc);
pthread_mutex_lock(&sc->mutex);
int ret = 0;
logd("TinaSoundDeviceStart(): sc->sound_status = %d",sc->sound_status);
if(sc->sound_status == STATUS_START){
logd("Sound device already start.");
pthread_mutex_unlock(&sc->mutex);
return ret;
}else if(sc->sound_status == STATUS_PAUSE){
if(snd_pcm_state(sc->alsa_handler) == SND_PCM_STATE_SUSPENDED){
logd("MSGTR_AO_ALSA_PcmInSuspendModeTryingResume");
while((ret = snd_pcm_resume(sc->alsa_handler)) == -EAGAIN){
sleep(1);
}
}
if(sc->alsa_can_pause){
if((ret = snd_pcm_pause(sc->alsa_handler, 0))<0){
loge("snd_pcm_pause failed:%s",strerror(errno));
pthread_mutex_unlock(&sc->mutex);
return ret;
}
}else{
if ((ret = snd_pcm_prepare(sc->alsa_handler)) < 0)
{
loge("snd_pcm_prepare failed:%s",strerror(errno));
pthread_mutex_unlock(&sc->mutex);
return ret;
}
}
sc->sound_status = STATUS_START;
}
else if(sc->sound_status == STATUS_STOP){
sc->alsa_fragcount = 8;
sc->chunk_size = 1024;//1024;
ret = openSoundDevice(sc, BLOCK_MODE);
logd("after openSoundDevice() ret = %d",ret);
if(ret >= 0){
ret = setSoundDeviceParams(sc);
if(ret < 0){
loge("setSoundDeviceParams fail , ret = %d",ret);
pthread_mutex_unlock(&sc->mutex);
return ret;
}
sc->sound_status = STATUS_START;
}
}
pthread_mutex_unlock(&sc->mutex);
return ret;
}
int TinaSoundDeviceStop(SoundCtrl* s){
int ret = 0;
SoundCtrlContext* sc;
sc = (SoundCtrlContext*)s;
assert(sc);
pthread_mutex_lock(&sc->mutex);
logd("TinaSoundDeviceStop():sc->sound_status = %d",sc->sound_status);
if(sc->sound_status == STATUS_STOP)
{
logd("Sound device already stopped.");
pthread_mutex_unlock(&sc->mutex);
return ret;
}else{
if ((ret = snd_pcm_drop(sc->alsa_handler)) < 0)
{
loge("snd_pcm_drop():MSGTR_AO_ALSA_PcmPrepareError");
pthread_mutex_unlock(&sc->mutex);
return ret;
}
if ((ret = snd_pcm_prepare(sc->alsa_handler)) < 0)
{
loge("snd_pcm_prepare():MSGTR_AO_ALSA_PcmPrepareError");
pthread_mutex_unlock(&sc->mutex);
return ret;
}
ret = closeSoundDevice(sc);
sc->sound_status = STATUS_STOP;
}
pthread_mutex_unlock(&sc->mutex);
return ret;
}
int TinaSoundDevicePause(SoundCtrl* s){
SoundCtrlContext* sc;
sc = (SoundCtrlContext*)s;
assert(sc);
pthread_mutex_lock(&sc->mutex);
int ret = 0;
logd("TinaSoundDevicePause(): sc->sound_status = %d",sc->sound_status);
if(sc->sound_status == STATUS_START){
if(sc->alsa_can_pause){
logd("alsa can pause,use snd_pcm_pause");
ret = snd_pcm_pause(sc->alsa_handler, 1);
if(ret<0){
loge("snd_pcm_pause failed:%s",strerror(errno));
pthread_mutex_unlock(&sc->mutex);
return ret;
}
}else{
logd("alsa can not pause,use snd_pcm_drop");
if ((ret = snd_pcm_drop(sc->alsa_handler)) < 0)
{
loge("snd_pcm_drop failed:%s",strerror(errno));
pthread_mutex_unlock(&sc->mutex);
return ret;
}
}
sc->sound_status = STATUS_PAUSE;
}else{
logd("TinaSoundDevicePause(): pause in an invalid status,status = %d",sc->sound_status);
}
pthread_mutex_unlock(&sc->mutex);
return ret;
}
int TinaSoundDeviceWrite(SoundCtrl* s, void* pData, int nDataSize){
int ret = 0;
SoundCtrlContext* sc;
sc = (SoundCtrlContext*)s;
assert(sc);
//TLOGD("TinaSoundDeviceWrite:sc->bytes_per_sample = %d\n",sc->bytes_per_sample);
if(sc->bytes_per_sample == 0){
sc->bytes_per_sample = 4;
}
if(sc->sound_status == STATUS_STOP || sc->sound_status == STATUS_PAUSE)
{
return ret;
}
//TLOGD("TinaSoundDeviceWrite>>> pData = %p , nDataSize = %d\n",pData,nDataSize);
int num_frames = nDataSize / sc->bytes_per_sample;
snd_pcm_sframes_t res = 0;
if (!sc->alsa_handler)
{
loge("MSGTR_AO_ALSA_DeviceConfigurationError");
return ret;
}
if (num_frames == 0){
loge("num_frames == 0");
return ret;
}
AudioGain audioGain;
audioGain.preamp = sc->mVolume;
audioGain.InputChan = (int)sc->nChannelNum;
audioGain.OutputChan = (int)sc->nChannelNum;
audioGain.InputLen = nDataSize;
audioGain.InputPtr = (short*)pData;
audioGain.OutputPtr = (short*)pData;
int gainRet = tina_do_AudioGain(&audioGain);
if(gainRet == 0){
loge("tina_do_AudioGain fail");
}
do {
//logd("snd_pcm_writei begin,nDataSize = %d",nDataSize);
res = snd_pcm_writei(sc->alsa_handler, pData, num_frames);
//logd("snd_pcm_writei finish,res = %ld",res);
if (res == -EINTR)
{
/* nothing to do */
res = 0;
} else if (res == -ESTRPIPE)
{ /* suspend */
logd("MSGTR_AO_ALSA_PcmInSuspendModeTryingResume");
while ((res = snd_pcm_resume(sc->alsa_handler)) == -EAGAIN)
sleep(1);
}
if (res < 0)
{
loge("MSGTR_AO_ALSA_WriteError,res = %ld",res);
if ((res = snd_pcm_prepare(sc->alsa_handler)) < 0)
{
loge("MSGTR_AO_ALSA_PcmPrepareError");
return res;
}
}
} while (res == 0);
return res < 0 ? res : res * sc->bytes_per_sample;
}
int TinaSoundDeviceReset(SoundCtrl* s){
logd("TinaSoundDeviceReset()");
return TinaSoundDeviceStop(s);
}
int TinaSoundDeviceGetCachedTime(SoundCtrl* s){
int ret = 0;
SoundCtrlContext* sc;
sc = (SoundCtrlContext*)s;
assert(sc);
//logd("TinaSoundDeviceGetCachedTime()");
if (sc->alsa_handler)
{
snd_pcm_sframes_t delay = 0;
//notify:snd_pcm_delay means the cache has how much data(the cache has been filled with pcm data),
//snd_pcm_avail_update means the free cache,
if ((ret = snd_pcm_delay(sc->alsa_handler, &delay)) < 0){
loge("TinaSoundDeviceGetCachedTime(),ret = %d , delay = %ld",ret,delay);
return 0;
}
//logd("TinaSoundDeviceGetCachedTime(),snd_pcm_delay>>> delay = %d",delay);
//delay = snd_pcm_avail_update(sc->alsa_handler);
//logd("TinaSoundDeviceGetCachedTime(), snd_pcm_avail_update >>> delay = %d\n",delay);
if (delay < 0) {
/* underrun - move the application pointer forward to catch up */
#if SND_LIB_VERSION >= 0x000901 /* snd_pcm_forward() exists since 0.9.0rc8 */
snd_pcm_forward(sc->alsa_handler, -delay);
#endif
delay = 0;
}
//logd("TinaSoundDeviceGetCachedTime(),ret = %d , delay = %ld",ret,delay);
ret = ((int)((float) delay * 1000000 / (float) sc->nSampleRate));
}
return ret;
}
SoundCtrl* TinaSoundDeviceInit_raw(void* raw_data,void* hdeccomp,RawCallback callback){
return NULL;
}
void TinaSoundDeviceRelease_raw(SoundCtrl* s){
}
void TinaSoundDeviceSetFormat_raw(SoundCtrl* s, unsigned int nSampleRate, unsigned int nChannelNum){
}
int TinaSoundDeviceStart_raw(SoundCtrl* s){
return 0;
}
int TinaSoundDeviceStop_raw(SoundCtrl* s){
return 0;
}
int TinaSoundDevicePause_raw(SoundCtrl* s){
return 0;
}
int TinaSoundDeviceWrite_raw(SoundCtrl* s, void* pData, int nDataSize){
return 0;
}
int TinaSoundDeviceReset_raw(SoundCtrl* s){
return 0;
}
int TinaSoundDeviceGetCachedTime_raw(SoundCtrl* s){
return 0;
}
int TinaSoundDeviceSetVolume(SoundCtrl* s, float volume){
SoundCtrlContext* sc;
sc = (SoundCtrlContext*)s;
assert(sc);
sc->mVolume = (int)volume;
logd("sc->gain = %d",sc->mVolume);
return 0;
}
int TinaSoundDeviceGetVolume(SoundCtrl* s, float *volume){
return 0;
}
int TinaSoundDeviceSetCallback(SoundCtrl* s, SndCallback callback, void* pUserData){
return 0;
}
}