1327 lines
42 KiB
C
Executable File
1327 lines
42 KiB
C
Executable File
/******************************************************************************
|
|
Copyright (C), 2001-2016, Allwinner Tech. Co., Ltd.
|
|
******************************************************************************
|
|
File Name : alsa_interface.c
|
|
Version : Initial Draft
|
|
Author : Allwinner BU3-PD2 Team
|
|
Created : 2016/04/19
|
|
Last Modified :
|
|
Description : mpi functions implement
|
|
Function List :
|
|
History :
|
|
******************************************************************************/
|
|
|
|
#define LOG_NDEBUG 0
|
|
#define LOG_TAG "alsa_interface"
|
|
#include <utils/plat_log.h>
|
|
#include <SystemBase.h>
|
|
#include <math.h>
|
|
#include <alsa_interface.h>
|
|
|
|
//static long gVolume;
|
|
static char aioDebugfsPath[256] = {0};
|
|
static unsigned int updateCnt = 0;
|
|
|
|
#define PERIOD_SIZE (256)
|
|
#define PERIOD_COUNT (4)
|
|
#define UPDATE_AIO_DEBUGFS_INFO(update_flag, item_flag, item, type, buf, path) \
|
|
do { \
|
|
if (update_flag & item_flag) { \
|
|
update_flag &= ~item_flag; \
|
|
memset(buf, 0, sizeof(buf)); \
|
|
sprintf(buf, "echo %x %d > %s\n", type, item, path); \
|
|
system(buf); \
|
|
} \
|
|
} while(0)
|
|
|
|
enum {
|
|
aiDEBit = 0,
|
|
aiCCBit = 1,
|
|
aiCTBit = 2,
|
|
aiSRBit = 3,
|
|
aiBWBit = 4,
|
|
aiTCBit = 5,
|
|
aiBMBit = 6,
|
|
aiVOLBit = 7,
|
|
|
|
aoDEBit = 8,
|
|
aoCCBit = 9,
|
|
aoCTBit = 10,
|
|
aoSRBit = 11,
|
|
aoBWBit = 12,
|
|
aoTCBit = 13,
|
|
aoBMBit = 14,
|
|
aoVOLBit = 15,
|
|
aoSoftVOLBit = 16,
|
|
};
|
|
|
|
// 0-none item need update; !0-some items need update.
|
|
static unsigned int aioDebugUpdataFlag = 0;
|
|
enum {
|
|
aiDevEnableFlag = 1<<aiDEBit, //ai dev updata falg
|
|
aiChnCntFlag = 1<<aiCCBit,
|
|
aiCardTypeFlag = 1<<aiCTBit,
|
|
aiSampleRateFlag = 1<<aiSRBit,
|
|
aiBitWidthFlag = 1<<aiBWBit,
|
|
aiTrackCntFlag = 1<<aiTCBit,
|
|
aibMuteFlag = 1<<aiBMBit,
|
|
aiVolumeFlag = 1<<aiVOLBit,
|
|
aoDevEnableFlag = 1<<aoDEBit, //ao dev updata falg
|
|
aoChnCntFlag = 1<<aoCCBit,
|
|
aoCardTypeFlag = 1<<aoCTBit,
|
|
aoSampleRateFlag = 1<<aoSRBit,
|
|
aoBitWidthFlag = 1<<aoBWBit,
|
|
aoTrackCntFlag = 1<<aoTCBit,
|
|
aobMuteFlag = 1<<aoBMBit,
|
|
aoVolumeFlag = 1<<aoVOLBit,
|
|
aoSoftVolumeFlag = 1<<aoSoftVOLBit,
|
|
};
|
|
|
|
//
|
|
//These params about DevEnable, CardType, SampleRate, BitWidth,
|
|
//will be auto updated by alsa kernel.So we should not update it at here.
|
|
//
|
|
static int aiDevEnable;
|
|
static int aiChnCnt;
|
|
static int aiCardType; //0-codec; 1-linein
|
|
static int aiSampleRate;
|
|
static int aiBitWidth;
|
|
static int aiTrackCnt;
|
|
static int aibMute;
|
|
static int aiVolume;
|
|
|
|
static int aoDevEnable;
|
|
static int aoChnCnt;
|
|
static int aoCardType; //0-codec; 1-hdmi
|
|
static int aoSampleRate;
|
|
static int aoBitWidth;
|
|
static int aoTrackCnt;
|
|
static int aobMute;
|
|
static int aoVolume;
|
|
static int aoSoftVolume;
|
|
static int aoPALevel; //0-low; 1-high
|
|
|
|
int UpdateDebugfsInfo()
|
|
{
|
|
//system("cat /proc/mounts | grep debugfs | awk '{print $2}'");
|
|
// /proc/sys/debug/mpp/sunxi-aio
|
|
if (aioDebugUpdataFlag)
|
|
{
|
|
if (updateCnt++ % 100 == 0)
|
|
{
|
|
FILE *stream;
|
|
char *cmd = "cat /proc/mounts | grep debugfs | awk '{print $2}'";
|
|
int nbytes;
|
|
|
|
stream = popen(cmd, "r");
|
|
if (stream == NULL)
|
|
return -1;
|
|
|
|
nbytes = fread(aioDebugfsPath, 1, sizeof(aioDebugfsPath), stream);
|
|
if (feof(stream) && nbytes > 0) {
|
|
|
|
/*
|
|
* Fixing the tailing whitespace.
|
|
*/
|
|
for(; nbytes > 0; nbytes--) {
|
|
if ((aioDebugfsPath[nbytes - 1] == 0x0a) ||
|
|
(aioDebugfsPath[nbytes - 1] == 0x20))
|
|
aioDebugfsPath[nbytes - 1] = '\0';
|
|
else
|
|
break;
|
|
}
|
|
|
|
strncat(aioDebugfsPath, "/mpp/sunxi-aio", sizeof(aioDebugfsPath)- nbytes);
|
|
}
|
|
pclose(stream);
|
|
}
|
|
|
|
if (access(aioDebugfsPath, F_OK) != 0) {
|
|
//alogv("sunxi-aio debugfs path not find!");
|
|
return -1;
|
|
}
|
|
|
|
char buf[256] = {0};
|
|
|
|
// format: echo X YZ > Path, here X must match code with aio.c in kernel
|
|
// ai dev info
|
|
//UPDATE_AIO_DEBUGFS_INFO(aioDebugUpdataFlag, aiDevEnableFlag, aiDevEnable, aiDEBit, buf, aioDebugfsPath);
|
|
UPDATE_AIO_DEBUGFS_INFO(aioDebugUpdataFlag, aiChnCntFlag, aiChnCnt, aiCCBit, buf, aioDebugfsPath);
|
|
//UPDATE_AIO_DEBUGFS_INFO(aioDebugUpdataFlag, aiCardTypeFlag, aiCardType, aiCTBit, buf, aioDebugfsPath);
|
|
//UPDATE_AIO_DEBUGFS_INFO(aioDebugUpdataFlag, aiSampleRateFlag, aiSampleRate, aiSRBit, buf, aioDebugfsPath);
|
|
//UPDATE_AIO_DEBUGFS_INFO(aioDebugUpdataFlag, aiBitWidthFlag, aiBitWidth, aiBWBit, buf, aioDebugfsPath);
|
|
UPDATE_AIO_DEBUGFS_INFO(aioDebugUpdataFlag, aiTrackCntFlag, aiTrackCnt, aiTCBit, buf, aioDebugfsPath);
|
|
UPDATE_AIO_DEBUGFS_INFO(aioDebugUpdataFlag, aibMuteFlag, aibMute, aiBMBit, buf, aioDebugfsPath);
|
|
UPDATE_AIO_DEBUGFS_INFO(aioDebugUpdataFlag, aiVolumeFlag, aiVolume, aiVOLBit, buf, aioDebugfsPath);
|
|
|
|
// ao dev info
|
|
//UPDATE_AIO_DEBUGFS_INFO(aioDebugUpdataFlag, aoDevEnableFlag, aoDevEnable, aoDEBit, buf, aioDebugfsPath);
|
|
UPDATE_AIO_DEBUGFS_INFO(aioDebugUpdataFlag, aoChnCntFlag, aoChnCnt, aoCCBit, buf, aioDebugfsPath);
|
|
//UPDATE_AIO_DEBUGFS_INFO(aioDebugUpdataFlag, aoCardTypeFlag, aoCardType, aoCTBit, buf, aioDebugfsPath);
|
|
//UPDATE_AIO_DEBUGFS_INFO(aioDebugUpdataFlag, aoSampleRateFlag, aoSampleRate, aoSRBit, buf, aioDebugfsPath);
|
|
// UPDATE_AIO_DEBUGFS_INFO(aioDebugUpdataFlag, aoBitWidthFlag, aoBitWidth, aoBWBit, buf, aioDebugfsPath);
|
|
UPDATE_AIO_DEBUGFS_INFO(aioDebugUpdataFlag, aoTrackCntFlag, aoTrackCnt, aoTCBit, buf, aioDebugfsPath);
|
|
UPDATE_AIO_DEBUGFS_INFO(aioDebugUpdataFlag, aobMuteFlag, aobMute, aoBMBit, buf, aioDebugfsPath);
|
|
UPDATE_AIO_DEBUGFS_INFO(aioDebugUpdataFlag, aoVolumeFlag, aoVolume, aoVOLBit, buf, aioDebugfsPath);
|
|
|
|
aioDebugUpdataFlag = 0;
|
|
/*
|
|
// format: echo X YZ > Path, here X must match code with aio.c
|
|
// ai dev info
|
|
if (aioDebugUpdataFlag & aiDevEnableFlag)
|
|
{
|
|
aioDebugUpdataFlag &= ~aiDevEnableFlag;
|
|
memset(buf, 0, sizeof(buf));
|
|
sprintf(buf, "echo 0 %d > %s\n", aiDevEnable, aioDebugfsPath);
|
|
system(buf);
|
|
}
|
|
if (aioDebugUpdataFlag & aiChnCntFlag)
|
|
{
|
|
aioDebugUpdataFlag &= ~aiChnCntFlag;
|
|
memset(buf, 0, sizeof(buf));
|
|
sprintf(buf, "echo 1 %d > %s\n", aiChnCnt, aioDebugfsPath);
|
|
system(buf);
|
|
}
|
|
if (aioDebugUpdataFlag & aiCardTypeFlag)
|
|
{
|
|
aioDebugUpdataFlag &= ~aiCardTypeFlag;
|
|
memset(buf, 0, sizeof(buf));
|
|
sprintf(buf, "echo 2 %d > %s\n", aiCardType, aioDebugfsPath);
|
|
system(buf);
|
|
}
|
|
if (aioDebugUpdataFlag & aiSampleRateFlag)
|
|
{
|
|
aioDebugUpdataFlag &= ~aiSampleRateFlag;
|
|
memset(buf, 0, sizeof(buf));
|
|
sprintf(buf, "echo 3 %d > %s\n", aiSampleRate, aioDebugfsPath);
|
|
system(buf);
|
|
}
|
|
if (aioDebugUpdataFlag & aiBitWidthFlag)
|
|
{
|
|
aioDebugUpdataFlag &= ~aiBitWidthFlag;
|
|
memset(buf, 0, sizeof(buf));
|
|
sprintf(buf, "echo 4 %d > %s\n", aiBitWidth, aioDebugfsPath);
|
|
system(buf);
|
|
}
|
|
if (aioDebugUpdataFlag & aiTrackCntFlag)
|
|
{
|
|
aioDebugUpdataFlag &= ~aiTrackCntFlag;
|
|
memset(buf, 0, sizeof(buf));
|
|
sprintf(buf, "echo 5 %d > %s\n", aiTrackCnt, aioDebugfsPath);
|
|
system(buf);
|
|
}
|
|
if (aioDebugUpdataFlag & aibMuteFlag)
|
|
{
|
|
aioDebugUpdataFlag &= ~aibMuteFlag;
|
|
memset(buf, 0, sizeof(buf));
|
|
sprintf(buf, "echo 6 %d > %s\n", aibMute, aioDebugfsPath);
|
|
system(buf);
|
|
}
|
|
if (aioDebugUpdataFlag & aiVolumeFlag)
|
|
{
|
|
aioDebugUpdataFlag &= ~aiVolumeFlag;
|
|
memset(buf, 0, sizeof(buf));
|
|
sprintf(buf, "echo 7 %d > %s\n", aiVolume, aioDebugfsPath);
|
|
system(buf);
|
|
}
|
|
|
|
// ao dev info
|
|
if (aioDebugUpdataFlag & aoDevEnableFlag)
|
|
{
|
|
aioDebugUpdataFlag &= ~aoDevEnableFlag;
|
|
memset(buf, 0, sizeof(buf));
|
|
sprintf(buf, "echo 8 %d > %s\n", aoDevEnable, aioDebugfsPath);
|
|
system(buf);
|
|
}
|
|
if (aioDebugUpdataFlag & aoChnCntFlag)
|
|
{
|
|
aioDebugUpdataFlag &= ~aoChnCntFlag;
|
|
memset(buf, 0, sizeof(buf));
|
|
sprintf(buf, "echo 9 %d > %s\n", aoChnCnt, aioDebugfsPath);
|
|
system(buf);
|
|
}
|
|
if (aioDebugUpdataFlag & aoCardTypeFlag)
|
|
{
|
|
aioDebugUpdataFlag &= ~aoCardTypeFlag;
|
|
memset(buf, 0, sizeof(buf));
|
|
sprintf(buf, "echo a %d > %s\n", aoCardType, aioDebugfsPath);
|
|
system(buf);
|
|
}
|
|
if (aioDebugUpdataFlag & aoSampleRateFlag)
|
|
{
|
|
aioDebugUpdataFlag &= ~aoSampleRateFlag;
|
|
memset(buf, 0, sizeof(buf));
|
|
sprintf(buf, "echo b %d > %s\n", aoSampleRate, aioDebugfsPath);
|
|
system(buf);
|
|
}
|
|
if (aioDebugUpdataFlag & aoBitWidthFlag)
|
|
{
|
|
aioDebugUpdataFlag &= ~aoBitWidthFlag;
|
|
memset(buf, 0, sizeof(buf));
|
|
sprintf(buf, "echo c %d > %s\n", aoBitWidth, aioDebugfsPath);
|
|
system(buf);
|
|
}
|
|
if (aioDebugUpdataFlag & aoTrackCntFlag)
|
|
{
|
|
aioDebugUpdataFlag &= ~aoTrackCntFlag;
|
|
memset(buf, 0, sizeof(buf));
|
|
sprintf(buf, "echo d %d > %s\n", aoTrackCnt, aioDebugfsPath);
|
|
system(buf);
|
|
}
|
|
if (aioDebugUpdataFlag & aobMuteFlag)
|
|
{
|
|
aioDebugUpdataFlag &= ~aobMuteFlag;
|
|
memset(buf, 0, sizeof(buf));
|
|
sprintf(buf, "echo e %d > %s\n", aibMute, aioDebugfsPath);
|
|
system(buf);
|
|
}
|
|
if (aioDebugUpdataFlag & aoVolumeFlag)
|
|
{
|
|
aioDebugUpdataFlag &= ~aoVolumeFlag;
|
|
memset(buf, 0, sizeof(buf));
|
|
sprintf(buf, "echo f %d > %s\n", aoVolume, aioDebugfsPath);
|
|
system(buf);
|
|
}
|
|
aioDebugUpdataFlag = 0;
|
|
*/
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int clearDebugfsInfoByHwDev(int pcmFlag)
|
|
{
|
|
if (pcmFlag == 0)
|
|
{
|
|
aiDevEnable = 0;
|
|
aiChnCnt = 0;
|
|
aiCardType = 0; //0-codec; 1-linein
|
|
aiSampleRate = 0;
|
|
aiBitWidth = 0;
|
|
aiTrackCnt = 0;
|
|
aibMute = 0;
|
|
aiVolume = 0;
|
|
aioDebugUpdataFlag |= 0xff;
|
|
}
|
|
else
|
|
{
|
|
aoDevEnable = 0;
|
|
aoChnCnt = 0;
|
|
aoCardType = 0; //0-codec; 1-hdmi
|
|
aoSampleRate = 0;
|
|
aoBitWidth = 0;
|
|
aoTrackCnt = 0;
|
|
aobMute = 0;
|
|
aoVolume = 0;
|
|
aioDebugUpdataFlag |= 0xff00;
|
|
}
|
|
UpdateDebugfsInfo();
|
|
|
|
return 0;
|
|
}
|
|
|
|
// pcmFlag: 0-cap update; 1-play update
|
|
void updateDebugfsByChnCnt(int pcmFlag, int cnt)
|
|
{
|
|
if (pcmFlag == 0)
|
|
{
|
|
aiChnCnt = cnt;
|
|
aioDebugUpdataFlag |= aiChnCntFlag;
|
|
}
|
|
else
|
|
{
|
|
aoChnCnt = cnt;
|
|
aioDebugUpdataFlag |= aoChnCntFlag;
|
|
}
|
|
}
|
|
|
|
int alsaSetPcmParams(PCM_CONFIG_S *pcmCfg)
|
|
{
|
|
snd_pcm_hw_params_t *params;
|
|
snd_pcm_sw_params_t *sw_params;
|
|
int err;
|
|
unsigned int rate, bufTime, periodTime;
|
|
// snd_pcm_uframes_t startThreshold, stopThreshold;
|
|
int dir = 0;
|
|
|
|
if (pcmCfg->handle == NULL) {
|
|
aloge("PCM is not open yet!");
|
|
return -1;
|
|
}
|
|
alogd("set pcm params");
|
|
|
|
snd_pcm_hw_params_alloca(¶ms);
|
|
// snd_pcm_sw_params_alloca(&swparams);
|
|
err = snd_pcm_hw_params_any(pcmCfg->handle, params);
|
|
if (err < 0) {
|
|
aloge("Broken configuration for this PCM: no configurations available");
|
|
return -1;
|
|
}
|
|
|
|
err = snd_pcm_hw_params_set_access(pcmCfg->handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
|
|
if (err < 0) {
|
|
aloge("Access type not available");
|
|
return -1;
|
|
}
|
|
|
|
err = snd_pcm_hw_params_set_format(pcmCfg->handle, params, pcmCfg->format);
|
|
if (err < 0) {
|
|
aloge("Sample format not available");
|
|
return -1;
|
|
}
|
|
|
|
err = snd_pcm_hw_params_set_channels(pcmCfg->handle, params, pcmCfg->chnCnt);
|
|
if (err < 0) {
|
|
aloge("Channels count not available");
|
|
return -1;
|
|
}
|
|
|
|
rate = pcmCfg->sampleRate;
|
|
err = snd_pcm_hw_params_set_rate_near(pcmCfg->handle, params, &pcmCfg->sampleRate, NULL);
|
|
if (err < 0) {
|
|
aloge("set_rate_near error!");
|
|
return -1;
|
|
}
|
|
if (rate != pcmCfg->sampleRate) {
|
|
alogd("required sample_rate %d is not supported, use %d instead", rate, pcmCfg->sampleRate);
|
|
}
|
|
|
|
snd_pcm_uframes_t periodSize = PERIOD_SIZE;
|
|
snd_pcm_uframes_t prePeriodSize = periodSize;
|
|
// alogd("size:%d count:%d\n", PERIOD_SIZE, PERIOD_COUNT);
|
|
err = snd_pcm_hw_params_set_period_size_near(pcmCfg->handle, params, &periodSize, &dir);
|
|
if (err < 0) {
|
|
aloge("set_period_size_near error!");
|
|
return -1;
|
|
}
|
|
if(prePeriodSize != periodSize)
|
|
{
|
|
alogw("Be careful! periodSize change:%d->%d", prePeriodSize, periodSize);
|
|
}
|
|
|
|
// double 1024-sample capacity -> 4
|
|
snd_pcm_uframes_t bufferSize = periodSize * PERIOD_COUNT;
|
|
snd_pcm_uframes_t preBufferSize = bufferSize;
|
|
err = snd_pcm_hw_params_set_buffer_size_near(pcmCfg->handle, params, &bufferSize);
|
|
if (err < 0) {
|
|
aloge("set_buffer_size_near error!");
|
|
return -1;
|
|
}
|
|
if(preBufferSize != bufferSize)
|
|
{
|
|
alogw("Be careful! bufferSize change:%d->%d", preBufferSize, bufferSize);
|
|
}
|
|
|
|
err = snd_pcm_hw_params(pcmCfg->handle, params);
|
|
if (err < 0) {
|
|
aloge("Unable to install hw params");
|
|
return -1;
|
|
}
|
|
|
|
snd_pcm_hw_params_get_period_size(params, &pcmCfg->chunkSize, 0);
|
|
snd_pcm_hw_params_get_buffer_size(params, &pcmCfg->bufferSize);
|
|
if (pcmCfg->chunkSize == pcmCfg->bufferSize) {
|
|
aloge("Can't use period equal to buffer size (%lu == %lu)", pcmCfg->chunkSize, pcmCfg->bufferSize);
|
|
return -1;
|
|
}
|
|
|
|
pcmCfg->bitsPerSample = snd_pcm_format_physical_width(pcmCfg->format);
|
|
pcmCfg->significantBitsPerSample = snd_pcm_format_width(pcmCfg->format);
|
|
pcmCfg->bitsPerFrame = pcmCfg->bitsPerSample * pcmCfg->chnCnt;
|
|
pcmCfg->chunkBytes = pcmCfg->chunkSize * pcmCfg->bitsPerFrame / 8;
|
|
|
|
alogd("----------------ALSA setting, pcm_stream:%d----------------", snd_pcm_stream(pcmCfg->handle));
|
|
alogd(">>Channels: %4d, BitWidth: %4d,phsical_w:%4d, SampRate: %4d", pcmCfg->chnCnt, pcmCfg->significantBitsPerSample, pcmCfg->bitsPerSample,pcmCfg->sampleRate);
|
|
alogd(">>ChunkBytes: %4d, ChunkSize: %4d, BufferSize: %4d", pcmCfg->chunkBytes, (int)pcmCfg->chunkSize, (int)pcmCfg->bufferSize);
|
|
|
|
/* SW params */
|
|
snd_pcm_sw_params_alloca(&sw_params);
|
|
snd_pcm_sw_params_current(pcmCfg->handle, sw_params);
|
|
if (snd_pcm_stream(pcmCfg->handle) == SND_PCM_STREAM_CAPTURE) {
|
|
snd_pcm_sw_params_set_start_threshold(pcmCfg->handle, sw_params, 1);
|
|
} else {
|
|
snd_pcm_uframes_t boundary = 0;
|
|
snd_pcm_sw_params_get_boundary(sw_params, &boundary);
|
|
// snd_pcm_uframes_t silence_size = 0;
|
|
// snd_pcm_sw_params_get_silence_size(sw_params, &silence_size);
|
|
// snd_pcm_uframes_t silence_threshold = 0;
|
|
// snd_pcm_sw_params_get_silence_threshold(sw_params, &silence_size);
|
|
alogd("SW play params get: boundary:0x%lx", boundary);
|
|
snd_pcm_sw_params_set_start_threshold(pcmCfg->handle, sw_params, pcmCfg->chunkSize);
|
|
/* set silence size, in order to fill silence data into ringbuffer */
|
|
snd_pcm_sw_params_set_silence_size(pcmCfg->handle, sw_params, boundary);
|
|
alogd("SW play params set: start_threshold:%ld, silence_size:0x%lx", pcmCfg->chunkSize, boundary);
|
|
}
|
|
snd_pcm_sw_params_set_stop_threshold(pcmCfg->handle, sw_params, pcmCfg->bufferSize);
|
|
snd_pcm_sw_params_set_avail_min(pcmCfg->handle, sw_params, pcmCfg->chunkSize);
|
|
err = snd_pcm_sw_params(pcmCfg->handle, sw_params);
|
|
if (err < 0) {
|
|
printf("Unable to install sw prams!\n");
|
|
return err;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int alsaOpenPcm(PCM_CONFIG_S *pcmCfg, const char *card, int pcmFlag)
|
|
{
|
|
snd_pcm_info_t *info;
|
|
snd_pcm_stream_t stream;
|
|
int err;
|
|
|
|
int open_mode = 0;
|
|
|
|
if (pcmCfg->handle != NULL) {
|
|
alogw("PCM is opened already!");
|
|
return 0;
|
|
}
|
|
alogd("open pcm! card:[%s], pcmFlag:[%d](0-cap;1-play)", card, pcmFlag);
|
|
|
|
// 0-cap; 1-play
|
|
stream = (pcmFlag == 0) ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK;
|
|
memset(pcmCfg->cardName, 0, sizeof(pcmCfg->cardName));
|
|
strncpy(pcmCfg->cardName, card, sizeof(pcmCfg->cardName));
|
|
|
|
snd_pcm_info_alloca(&info);
|
|
|
|
// open_mode |= SND_PCM_NO_AUTO_RESAMPLE; // not to used the auto resample
|
|
err = snd_pcm_open(&pcmCfg->handle, card, stream, open_mode);
|
|
if (err < 0) {
|
|
aloge("fatal error! card[%s] audio open error: %s", card, snd_strerror(err));
|
|
char *pTestMem = malloc(8*1024);
|
|
if(pTestMem)
|
|
{
|
|
free(pTestMem);
|
|
pTestMem = NULL;
|
|
}
|
|
else
|
|
{
|
|
aloge("fatal error! malloc fail, no memory now!");
|
|
}
|
|
// system("cat /proc/meminfo")
|
|
return -1;
|
|
}
|
|
if ((err = snd_pcm_info(pcmCfg->handle, info)) < 0) {
|
|
aloge("snd_pcm_info error: %s", snd_strerror(err));
|
|
return -1;
|
|
}
|
|
|
|
if(0 == pcmCfg->snd_card_id)
|
|
{
|
|
if (stream == SND_PCM_STREAM_CAPTURE) {
|
|
aiDevEnable = 1;
|
|
aiCardType = 0;
|
|
aiSampleRate = pcmCfg->sampleRate;
|
|
aiBitWidth = pcmCfg->bitsPerSample;
|
|
aiTrackCnt = pcmCfg->chnCnt;
|
|
aioDebugUpdataFlag |= aiDevEnableFlag | aiCardTypeFlag | aiSampleRateFlag | aiBitWidthFlag | aiTrackCntFlag | aiVolumeFlag | aibMuteFlag;
|
|
} else if (stream == SND_PCM_STREAM_PLAYBACK) {
|
|
aoDevEnable = 1;
|
|
aoCardType = strncmp(card, "default", 7)==0?0:1;
|
|
aoSampleRate = pcmCfg->sampleRate;
|
|
aoBitWidth = pcmCfg->bitsPerSample;
|
|
aoTrackCnt= pcmCfg->chnCnt;
|
|
aioDebugUpdataFlag |= aoDevEnableFlag | aoCardTypeFlag | aoSampleRateFlag | aoBitWidthFlag | aoTrackCntFlag | aoVolumeFlag | aobMuteFlag;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void alsaClosePcm(PCM_CONFIG_S *pcmCfg, int pcmFlag)
|
|
{
|
|
//alogd("close pcm");
|
|
clearDebugfsInfoByHwDev(pcmFlag);
|
|
|
|
if (pcmCfg->handle == NULL) {
|
|
aloge("PCM is not open yet!");
|
|
return;
|
|
}
|
|
snd_pcm_close(pcmCfg->handle);
|
|
pcmCfg->handle = NULL;
|
|
}
|
|
|
|
void alsaPreparePcm(PCM_CONFIG_S *pcmCfg)
|
|
{
|
|
alogd("prepare pcm");
|
|
|
|
if (pcmCfg->handle == NULL) {
|
|
aloge("PCM is not open yet!");
|
|
return;
|
|
}
|
|
snd_pcm_prepare(pcmCfg->handle);
|
|
}
|
|
|
|
|
|
ssize_t alsaReadPcm(PCM_CONFIG_S *pcmCfg, void *data, size_t rcount)
|
|
{
|
|
ssize_t ret;
|
|
ssize_t result = 0;
|
|
int err = 0;
|
|
char cardName[sizeof(pcmCfg->cardName)] = {0};
|
|
|
|
if (rcount != pcmCfg->chunkSize)
|
|
rcount = pcmCfg->chunkSize;
|
|
|
|
if (pcmCfg == NULL || data == NULL) {
|
|
aloge("invalid input parameter(pcmCfg=%p, data=%p)!", pcmCfg, data);
|
|
return -1;
|
|
}
|
|
|
|
while (rcount > 0) {
|
|
/* if(0 == pcmCfg->snd_card_id) // bug fixing,consume too much time to excute shell command,when debugfs is not mounted.
|
|
{
|
|
UpdateDebugfsInfo();
|
|
} */
|
|
ret = snd_pcm_readi(pcmCfg->handle, data, rcount);
|
|
if (ret == -EAGAIN || (ret >= 0 && (size_t)ret < rcount)) {
|
|
snd_pcm_wait(pcmCfg->handle, 100);
|
|
} else if (ret == -EPIPE) {
|
|
aloge("aec_alsa_overflow_xrun:%d-%lld-(%s)!", pcmCfg->snd_card_id, CDX_GetSysTimeUsMonotonic(), strerror(errno));
|
|
snd_pcm_prepare(pcmCfg->handle);
|
|
if(pcmCfg->read_pcm_aec) // for aec condition,need to return directly and re-trigger cap dma again
|
|
{
|
|
// aloge("aec_rtn_drtly");
|
|
// return ret;
|
|
aloge("fatal error! read pcm aec meet EPIPE.");
|
|
}
|
|
} else if (ret == -ESTRPIPE) {
|
|
alogd("need recover(%s)!", strerror(errno));
|
|
snd_pcm_recover(pcmCfg->handle, ret, 0);
|
|
} else if (ret < 0) {
|
|
aloge("read error: %s", snd_strerror(ret));
|
|
return -1;
|
|
}
|
|
|
|
if (ret > 0) {
|
|
result += ret;
|
|
rcount -= ret;
|
|
data += ret * pcmCfg->bitsPerFrame / 8;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
ssize_t alsaWritePcm(PCM_CONFIG_S *pcmCfg, void *data, size_t wcount)
|
|
{
|
|
ssize_t ret;
|
|
ssize_t result = 0;
|
|
int err = 0;
|
|
char cardName[sizeof(pcmCfg->cardName)] = {0};
|
|
|
|
if (snd_pcm_state(pcmCfg->handle) == SND_PCM_STATE_SUSPENDED) {
|
|
while ((err = snd_pcm_resume(pcmCfg->handle)) == -EAGAIN) {
|
|
aloge("snd_pcm_resume again!");
|
|
sleep(1);
|
|
}
|
|
switch(snd_pcm_state(pcmCfg->handle))
|
|
{
|
|
case SND_PCM_STATE_XRUN:
|
|
{
|
|
snd_pcm_drop(pcmCfg->handle);
|
|
break;
|
|
}
|
|
case SND_PCM_STATE_SETUP:
|
|
break;
|
|
default:
|
|
{
|
|
alogw("pcm_lib_state:%s",snd_pcm_state_name(snd_pcm_state(pcmCfg->handle)));
|
|
snd_pcm_prepare(pcmCfg->handle);
|
|
break;
|
|
}
|
|
}
|
|
alsaSetPcmParams(pcmCfg);
|
|
}
|
|
|
|
while (wcount > 0) {
|
|
// if(0 == pcmCfg->snd_card_id)
|
|
// {
|
|
// UpdateDebugfsInfo();
|
|
// }
|
|
if (snd_pcm_state(pcmCfg->handle) == SND_PCM_STATE_SETUP) {
|
|
snd_pcm_prepare(pcmCfg->handle);
|
|
}
|
|
ret = snd_pcm_writei(pcmCfg->handle, data, wcount);
|
|
if (ret == -EAGAIN || (ret >= 0 && (size_t)ret < wcount)) {
|
|
snd_pcm_wait(pcmCfg->handle, 100);
|
|
} else if (ret == -EPIPE) {
|
|
//alogv("xrun!");
|
|
snd_pcm_prepare(pcmCfg->handle);
|
|
} else if (ret == -EBADFD) {
|
|
//alogw("careful! current pcm state: %d", snd_pcm_state(pcmCfg->handle));
|
|
snd_pcm_prepare(pcmCfg->handle);
|
|
} else if (ret == -ESTRPIPE) {
|
|
aloge("need recover!");
|
|
snd_pcm_recover(pcmCfg->handle, ret, 0);
|
|
} else if (ret < 0) {
|
|
aloge("write error! ret:%d, %s", ret, snd_strerror(ret));
|
|
//0-cap; 1-play
|
|
alsaClosePcm(pcmCfg, 1);
|
|
//FIXME: reopen
|
|
aloge("cardName:[%s], pcmFlag:[play]", pcmCfg->cardName);
|
|
strncpy(cardName, pcmCfg->cardName, sizeof(pcmCfg->cardName));
|
|
ret = alsaOpenPcm(pcmCfg, cardName, 1);
|
|
if (ret < 0) {
|
|
aloge("alsaOpenPcm failed!");
|
|
return ret;
|
|
}
|
|
ret = alsaSetPcmParams(pcmCfg);
|
|
if (ret < 0) {
|
|
aloge("alsa SetPcmParams failed!");
|
|
return ret;
|
|
}
|
|
if (pcmCfg->handle != NULL) {
|
|
snd_pcm_reset(pcmCfg->handle);
|
|
snd_pcm_prepare(pcmCfg->handle);
|
|
snd_pcm_start(pcmCfg->handle);
|
|
}
|
|
alogd("set pcm prepare finished!");
|
|
return ret;
|
|
}
|
|
|
|
if (ret > 0) {
|
|
result += ret;
|
|
wcount -= ret;
|
|
data += ret * pcmCfg->bitsPerFrame / 8;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int alsaDrainPcm(PCM_CONFIG_S *pcmCfg)
|
|
{
|
|
int err = 0;
|
|
|
|
err = snd_pcm_drain(pcmCfg->handle);
|
|
if (err != 0){
|
|
aloge("drain pcm err! err=%d", err);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int alsaOpenMixer(AIO_MIXER_S *mixer, const char *card)
|
|
{
|
|
snd_mixer_selem_id_t *sid;
|
|
snd_mixer_elem_t *elem;
|
|
int err = 0;
|
|
|
|
if (mixer->handle != NULL) {
|
|
return 0;
|
|
}
|
|
alogd("open mixer:%s",card);
|
|
|
|
snd_mixer_selem_id_alloca(&sid);
|
|
|
|
err = snd_mixer_open(&mixer->handle, 0);
|
|
if (err < 0) {
|
|
aloge("Mixer %s open error: %s\n", card, snd_strerror(err));
|
|
return err;
|
|
}
|
|
|
|
err = snd_mixer_attach(mixer->handle, card);
|
|
if (err < 0) {
|
|
aloge("Mixer %s attach error: %s\n", card, snd_strerror(err));
|
|
goto ERROR;
|
|
}
|
|
|
|
err = snd_mixer_selem_register(mixer->handle, NULL, NULL);
|
|
if (err < 0) {
|
|
aloge("Mixer %s register error: %s\n", card, snd_strerror(err));
|
|
goto ERROR;
|
|
}
|
|
|
|
err = snd_mixer_load(mixer->handle);
|
|
if (err < 0) {
|
|
aloge("Mixer %s load error: %s\n", card, snd_strerror(err));
|
|
goto ERROR;
|
|
}
|
|
|
|
for (elem = snd_mixer_first_elem(mixer->handle); elem; elem = snd_mixer_elem_next(elem))
|
|
{
|
|
snd_mixer_selem_get_id(elem, sid);
|
|
//snd_mixer_selem_set_playback_volume_range(elem, AUDIO_VOLUME_MIN, AUDIO_VOLUME_MAX);
|
|
//snd_mixer_selem_set_capture_volume_range(elem, AUDIO_VOLUME_MIN, AUDIO_VOLUME_MAX);
|
|
// open lineout and mic switch
|
|
const char *elem_name = snd_mixer_selem_get_name(elem);
|
|
alogv("alsa_elem:%s",elem_name);
|
|
|
|
if ( !strcmp(elem_name, AUDIO_ADC_MIC1_SWITCH) )
|
|
{
|
|
snd_mixer_selem_set_playback_switch(elem, 0, 1);
|
|
}
|
|
else if ( !strcmp(elem_name, AUDIO_ADC_MIC2_SWITCH) )
|
|
{
|
|
snd_mixer_selem_set_playback_switch(elem, 0, 0);// disable mic2 by default
|
|
}
|
|
else if(!strcmp(elem_name, AUDIO_LINEIN_SWITCH))
|
|
{
|
|
snd_mixer_selem_set_playback_switch(elem, 0, 0);
|
|
}
|
|
else if (!strcmp(elem_name, AUDIO_LINEOUT_VOL))
|
|
{
|
|
// lineout volume. 0x1f~0x02 : 0dB~-43.5dB, 1.5dB/step. 27 : -6dB.
|
|
// user had better not change this ctrls, nor will cause wave distort!
|
|
long vol_val = 27;
|
|
snd_mixer_selem_set_playback_volume(elem, 0, vol_val);
|
|
alogd("set playback vol_val to value: %ld", vol_val);
|
|
aoVolume = 100*vol_val/AUDIO_VOLUME_MAX;
|
|
if (aiDevEnable) {
|
|
aioDebugUpdataFlag |= aoVolumeFlag;
|
|
}
|
|
}
|
|
else if (!strcmp(elem_name, AUDIO_LINEOUT_SOFT_VOL))
|
|
{
|
|
// AW_MPI_AO_SetSoftVolume() scope is :[-52, 50].
|
|
// lineout soft volume. [0,255] : -26dB~25dB, we set 130. (25-(-26))/255 = 0.2dB
|
|
long vol_val = 130;
|
|
snd_mixer_selem_set_playback_volume(elem, 0, vol_val);
|
|
aoSoftVolume = (AUDIO_SOFT_VOLUME_MPP_SCOPE_MAX-AUDIO_SOFT_VOLUME_MPP_SCOPE_MIN)*(vol_val-AUDIO_SOFT_VOLUME_MIN)
|
|
/(AUDIO_SOFT_VOLUME_MAX-AUDIO_SOFT_VOLUME_MIN) + AUDIO_SOFT_VOLUME_MPP_SCOPE_MIN;
|
|
alogd("set playback soft_vol val to value: %ld, mppValue:%d", vol_val, aoSoftVolume);
|
|
if (aiDevEnable)
|
|
{
|
|
aioDebugUpdataFlag |= aoSoftVolumeFlag;
|
|
}
|
|
}
|
|
else if (!strcmp(elem_name, AUDIO_LINEOUT_SWITCH))
|
|
{
|
|
snd_mixer_selem_set_playback_switch(elem, 0, 1);
|
|
}
|
|
else if (!strcmp(elem_name, AUDIO_LINEOUT_MUX))
|
|
{
|
|
snd_mixer_selem_set_enum_item(elem, 0, 1); // increase play volume when amplifier differential input
|
|
}
|
|
else if(!strcmp(elem_name, AUDIO_PA_SWITCH)){
|
|
snd_mixer_selem_set_playback_switch(elem, 0, 1);
|
|
aoPALevel = 0;
|
|
}
|
|
}
|
|
|
|
|
|
return err;
|
|
|
|
ERROR:
|
|
snd_mixer_close(mixer->handle);
|
|
mixer->handle = NULL;
|
|
return err;
|
|
}
|
|
|
|
void alsaCloseMixer(AIO_MIXER_S *mixer)
|
|
{
|
|
if (mixer->handle == NULL) {
|
|
return;
|
|
}
|
|
alogd("close mixer[%p].card_id:%d", mixer, mixer->snd_card_id);
|
|
|
|
snd_mixer_close(mixer->handle);
|
|
mixer->handle = NULL;
|
|
}
|
|
|
|
int alsaMixerSetMicXEnable(AIO_MIXER_S *mixer, int nMicId, int value)
|
|
{
|
|
int err = 0;
|
|
|
|
if (mixer->handle == NULL) {
|
|
return -1;
|
|
}
|
|
char strMicXSwitch[32] = {'\0'};
|
|
sprintf(strMicXSwitch, "MIC%d", nMicId); //query 'MIC1 Switch' get 'MIC1' here.
|
|
snd_mixer_elem_t *elem;
|
|
for (elem = snd_mixer_first_elem(mixer->handle); elem; elem = snd_mixer_elem_next(elem)) {
|
|
const char *elem_name = snd_mixer_selem_get_name(elem);
|
|
if(!strcmp(elem_name, strMicXSwitch)) //AUDIO_ADC_MIC2_SWITCH
|
|
{
|
|
alogd("snd_card[%d]-mic%d_switch:%s-%d", mixer->snd_card_id, nMicId, elem_name, value);
|
|
err = snd_mixer_selem_set_playback_switch(elem, 0, value);
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int alsaMixerSetLineInEnable(AIO_MIXER_S *mixer, int value)
|
|
{
|
|
int err = 0;
|
|
|
|
if (mixer->handle == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
snd_mixer_elem_t *elem;
|
|
for (elem = snd_mixer_first_elem(mixer->handle); elem; elem = snd_mixer_elem_next(elem)) {
|
|
const char *elem_name = snd_mixer_selem_get_name(elem);
|
|
|
|
if(!strcmp(elem_name, AUDIO_LINEIN_SWITCH))
|
|
{
|
|
aloge("aec_elem_linein_switch:%s-%d",elem_name,value);
|
|
err = snd_mixer_selem_set_playback_switch(elem, 0, value);
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
int alsaMixerSetCapPlaySyncMode(AIO_MIXER_S *mixer, int value)
|
|
{
|
|
int err = 0;
|
|
|
|
if (mixer->handle == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
snd_mixer_elem_t *elem;
|
|
for (elem = snd_mixer_first_elem(mixer->handle); elem; elem = snd_mixer_elem_next(elem)) {
|
|
const char *elem_name = snd_mixer_selem_get_name(elem);
|
|
|
|
if(!strcmp(elem_name, AUDIO_CAP_PLAY_SYNC_MODE))
|
|
{
|
|
alogd("aec_elem_sync_mode_switch:%s-%d",elem_name,value);
|
|
snd_mixer_selem_set_enum_item(elem, 0, value);
|
|
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
/* to set drc function of dac */
|
|
int alsaMixerSetAudioCodecDacDrc(AIO_MIXER_S *mixer, int value)
|
|
{
|
|
int err = 0;
|
|
|
|
if (mixer->handle == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
snd_mixer_elem_t *elem;
|
|
for (elem = snd_mixer_first_elem(mixer->handle); elem; elem = snd_mixer_elem_next(elem)) {
|
|
const char *elem_name = snd_mixer_selem_get_name(elem);
|
|
|
|
if(!strcmp(elem_name, AUDIO_DACDRC_EN))
|
|
{
|
|
aloge("audio_codec_elem_dac_drc_en:%s-%d",elem_name,value);
|
|
snd_mixer_selem_set_enum_item(elem, 0, value);
|
|
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
/* to set drc function of adc */
|
|
int alsaMixerSetAudioCodecAdcDrc(AIO_MIXER_S *mixer, int value)
|
|
{
|
|
int err = 0;
|
|
|
|
if (mixer->handle == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
snd_mixer_elem_t *elem;
|
|
for (elem = snd_mixer_first_elem(mixer->handle); elem; elem = snd_mixer_elem_next(elem)) {
|
|
const char *elem_name = snd_mixer_selem_get_name(elem);
|
|
|
|
if(!strcmp(elem_name, AUDIO_ADCDRC_EN))
|
|
{
|
|
aloge("audio_codec_elem_adc_drc_en:%s-%d",elem_name,value);
|
|
snd_mixer_selem_set_enum_item(elem, 0, value);
|
|
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
/* to set hpf function of dac */
|
|
int alsaMixerSetAudioCodecDacHpf(AIO_MIXER_S *mixer, int value)
|
|
{
|
|
int err = 0;
|
|
|
|
if (mixer->handle == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
snd_mixer_elem_t *elem;
|
|
for (elem = snd_mixer_first_elem(mixer->handle); elem; elem = snd_mixer_elem_next(elem)) {
|
|
const char *elem_name = snd_mixer_selem_get_name(elem);
|
|
|
|
if(!strcmp(elem_name, AUDIO_DACHPF_EN))
|
|
{
|
|
aloge("audio_codec_elem_dac_hpf_en:%s-%d",elem_name,value);
|
|
snd_mixer_selem_set_enum_item(elem, 0, value);
|
|
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
/* to set hpf function of adc */
|
|
int alsaMixerSetAudioCodecAdcHpf(AIO_MIXER_S *mixer, int value)
|
|
{
|
|
int err = 0;
|
|
|
|
if (mixer->handle == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
snd_mixer_elem_t *elem;
|
|
for (elem = snd_mixer_first_elem(mixer->handle); elem; elem = snd_mixer_elem_next(elem)) {
|
|
const char *elem_name = snd_mixer_selem_get_name(elem);
|
|
|
|
if(!strcmp(elem_name, AUDIO_ADCHPF_EN))
|
|
{
|
|
aloge("audio_codec_elem_adc_hpf_en:%s-%d",elem_name,value);
|
|
snd_mixer_selem_set_enum_item(elem, 0, value);
|
|
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int alsaMixerSetAudioCodecHubMode(AIO_MIXER_S *mixer, int value)
|
|
{
|
|
int err = 0;
|
|
|
|
if (mixer->handle == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
snd_mixer_elem_t *elem;
|
|
for (elem = snd_mixer_first_elem(mixer->handle); elem; elem = snd_mixer_elem_next(elem)) {
|
|
const char *elem_name = snd_mixer_selem_get_name(elem);
|
|
|
|
if(!strcmp(elem_name, AUDIO_CODEC_HUB_MODE))
|
|
{
|
|
alogd("aec_elem_audio_codec_hub_mode:%s-%d",elem_name,value);
|
|
snd_mixer_selem_set_enum_item(elem, 0, value);
|
|
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int alsaMixerSetDAudio0HubMode(AIO_MIXER_S *mixer, int value)
|
|
{
|
|
int err = 0;
|
|
|
|
if (mixer->handle == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
snd_mixer_elem_t *elem;
|
|
for (elem = snd_mixer_first_elem(mixer->handle); elem; elem = snd_mixer_elem_next(elem)) {
|
|
const char *elem_name = snd_mixer_selem_get_name(elem);
|
|
|
|
if(!strcmp(elem_name, DAUDIo0_HUB_MODE))
|
|
{
|
|
alogd("aec_elem_daudio0_hub_mode:%s-%d",elem_name,value);
|
|
snd_mixer_selem_set_enum_item(elem, 0, value);
|
|
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int alsaMixerSetDAudio0LoopBackEn(AIO_MIXER_S *mixer, int value)
|
|
{
|
|
int err = 0;
|
|
|
|
if (mixer->handle == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
snd_mixer_elem_t *elem;
|
|
for (elem = snd_mixer_first_elem(mixer->handle); elem; elem = snd_mixer_elem_next(elem)) {
|
|
const char *elem_name = snd_mixer_selem_get_name(elem);
|
|
|
|
if(!strcmp(elem_name, DAUDIo0_LOOPBACK_EN))
|
|
{
|
|
alogd("aec_elem_daudio0_loopback_en:%s-%d",elem_name,value);
|
|
snd_mixer_selem_set_playback_switch(elem, 0, value);
|
|
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int alsaMixerSetVolume(AIO_MIXER_S *mixer, int playFlag, long value)
|
|
{
|
|
int err = 0;
|
|
|
|
if (mixer->handle == NULL) {
|
|
return -1;
|
|
}
|
|
if (value < 0 || value > 100) {
|
|
aloge("want to setAIOVol[0,100], playFlag[%d], but usr value=%ld is invalid!", playFlag, value);
|
|
return -1;
|
|
}
|
|
|
|
snd_mixer_elem_t *elem;
|
|
for (elem = snd_mixer_first_elem(mixer->handle); elem; elem = snd_mixer_elem_next(elem)) {
|
|
const char *elem_name = snd_mixer_selem_get_name(elem);
|
|
if (playFlag && !strcmp(elem_name, AUDIO_LINEOUT_VOL)) {
|
|
long realVol = value*AUDIO_VOLUME_MAX/100;
|
|
err = snd_mixer_selem_set_playback_volume(elem, 0, realVol);
|
|
alogd("snd_card:%d, elem_name:%s, playback setVolume:%ld, err:%d", mixer->snd_card_id, elem_name, realVol, err);
|
|
aoVolume = value;
|
|
if (aoDevEnable) {
|
|
aioDebugUpdataFlag |= aoVolumeFlag;
|
|
}
|
|
break;
|
|
}
|
|
else if(!playFlag && (!strcmp(elem_name, AUDIO_MIC1_MAIN_GAIN) || !strcmp(elem_name, AUDIO_MIC2_MAIN_GAIN)))
|
|
{
|
|
long realVol = value*AUDIO_VOLUME_MAX/100;
|
|
err = snd_mixer_selem_set_playback_volume(elem, 0, realVol);
|
|
alogd("snd_card:%d, elem_name:%s, set_ai_main_gain:%d", mixer->snd_card_id, elem_name, realVol);
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int alsaMixerGetVolume(AIO_MIXER_S *mixer, int playFlag, long *value)
|
|
{
|
|
int err = 0;
|
|
|
|
if (mixer->handle == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
snd_mixer_elem_t *elem;
|
|
for (elem = snd_mixer_first_elem(mixer->handle); elem; elem = snd_mixer_elem_next(elem)) {
|
|
const char *elem_name = snd_mixer_selem_get_name(elem);
|
|
if (playFlag && !strcmp(elem_name, AUDIO_LINEOUT_VOL)) {
|
|
long realVal;
|
|
err = snd_mixer_selem_get_playback_volume(elem, 0, &realVal);
|
|
// scale from AUDIO_VOLUME_MAX to 100
|
|
*value = realVal * 100 / AUDIO_VOLUME_MAX;
|
|
alogd("playback getVolume:%ld, dst:%ld, err:%d", realVal, *value, err);
|
|
aoVolume = *value;
|
|
if (aoDevEnable) {
|
|
aioDebugUpdataFlag |= aoVolumeFlag;
|
|
}
|
|
break;
|
|
}
|
|
else if(!playFlag && !strcmp(elem_name, AUDIO_MIC1_MAIN_GAIN))
|
|
{
|
|
long realVal;
|
|
err = snd_mixer_selem_get_playback_volume(elem, 0, &realVal);
|
|
|
|
// scale from AUDIO_VOLUME_MAX to 100
|
|
*value = realVal * 100 / AUDIO_VOLUME_MAX;
|
|
alogd("get_ai_main_gain:%ld, dst:%ld, err:%d", realVal, *value, err);
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int alsaMixerSetSoftVolume(AIO_MIXER_S *mixer, int playFlag, long value)
|
|
{
|
|
int err = 0;
|
|
|
|
if (mixer->handle == NULL)
|
|
{
|
|
aloge("fatal error! mixer handle is NULL");
|
|
return -1;
|
|
}
|
|
if(!playFlag)
|
|
{
|
|
aloge("fatal error! softVolume do not support capture");
|
|
return -1;
|
|
}
|
|
if (value < AUDIO_SOFT_VOLUME_MPP_SCOPE_MIN || value > AUDIO_SOFT_VOLUME_MPP_SCOPE_MAX)
|
|
{
|
|
aloge("want to setAIOSoftVol[%d,%d], playFlag[%d], but usr value=%ld is invalid!",
|
|
AUDIO_SOFT_VOLUME_MPP_SCOPE_MIN, AUDIO_SOFT_VOLUME_MPP_SCOPE_MAX, playFlag, value);
|
|
return -1;
|
|
}
|
|
|
|
snd_mixer_elem_t *elem;
|
|
err = -1;
|
|
int bFindMixerElemFlag = 0;
|
|
for (elem = snd_mixer_first_elem(mixer->handle); elem; elem = snd_mixer_elem_next(elem))
|
|
{
|
|
const char *elem_name = snd_mixer_selem_get_name(elem);
|
|
if (playFlag && !strcmp(elem_name, AUDIO_LINEOUT_SOFT_VOL))
|
|
{
|
|
bFindMixerElemFlag = 1;
|
|
int nSoftVolValue = value;
|
|
double Vol = (double)(nSoftVolValue-AUDIO_SOFT_VOLUME_MPP_SCOPE_MIN)/(AUDIO_SOFT_VOLUME_MPP_SCOPE_MAX-AUDIO_SOFT_VOLUME_MPP_SCOPE_MIN)
|
|
*(AUDIO_SOFT_VOLUME_MAX-AUDIO_SOFT_VOLUME_MIN) + AUDIO_SOFT_VOLUME_MIN;
|
|
long realVol = floor(Vol);
|
|
err = snd_mixer_selem_set_playback_volume(elem, 0, realVol);
|
|
alogd("playback setSoftVolume:%ld(%lf-%ld), err:%d", realVol, Vol, value, err);
|
|
aoSoftVolume = value;
|
|
if (aoDevEnable) {
|
|
aioDebugUpdataFlag |= aoSoftVolumeFlag;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if(0 == bFindMixerElemFlag)
|
|
{
|
|
aloge("fatal error! can not find mixer_elem:[%s]", AUDIO_LINEOUT_SOFT_VOL);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int alsaMixerGetSoftVolume(AIO_MIXER_S *mixer, int playFlag, long *value)
|
|
{
|
|
int err = 0;
|
|
|
|
if (mixer->handle == NULL)
|
|
{
|
|
aloge("fatal error! mixer handle is NULL");
|
|
return -1;
|
|
}
|
|
if(!playFlag)
|
|
{
|
|
aloge("fatal error! softVolume do not support capture");
|
|
return -1;
|
|
}
|
|
|
|
snd_mixer_elem_t *elem;
|
|
err = -1;
|
|
for (elem = snd_mixer_first_elem(mixer->handle); elem; elem = snd_mixer_elem_next(elem))
|
|
{
|
|
const char *elem_name = snd_mixer_selem_get_name(elem);
|
|
if (playFlag && !strcmp(elem_name, AUDIO_LINEOUT_SOFT_VOL))
|
|
{
|
|
long realVal;
|
|
err = snd_mixer_selem_get_playback_volume(elem, 0, &realVal);
|
|
int nSoftVolValue = (AUDIO_SOFT_VOLUME_MPP_SCOPE_MAX-AUDIO_SOFT_VOLUME_MPP_SCOPE_MIN)*(realVal-AUDIO_SOFT_VOLUME_MIN)
|
|
/(AUDIO_SOFT_VOLUME_MAX-AUDIO_SOFT_VOLUME_MIN) + AUDIO_SOFT_VOLUME_MPP_SCOPE_MIN;
|
|
*value = nSoftVolValue;
|
|
alogd("playback getSoftVolume:%ld(%ld), err:%d", realVal, *value, err);
|
|
aoSoftVolume = *value;
|
|
if (aoDevEnable)
|
|
{
|
|
aioDebugUpdataFlag |= aoSoftVolumeFlag;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int alsaMixerSetMute(AIO_MIXER_S *mixer, int playFlag, int bEnable)
|
|
{
|
|
int err = 0;
|
|
|
|
if (mixer->handle == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
snd_mixer_elem_t *elem;
|
|
for (elem = snd_mixer_first_elem(mixer->handle); elem; elem = snd_mixer_elem_next(elem)) {
|
|
const char *elem_name = snd_mixer_selem_get_name(elem);
|
|
if (playFlag && !strcmp(elem_name, AUDIO_PA_SWITCH/*AUDIO_LINEOUT_SWITCH*/)) {
|
|
alogd("set player master-volume switch state: %d", bEnable);
|
|
if (bEnable) {
|
|
err = snd_mixer_selem_set_playback_switch(elem, 0, 0);
|
|
} else {
|
|
err = snd_mixer_selem_set_playback_switch(elem, 0, 1);
|
|
}
|
|
aobMute = bEnable;
|
|
if (aoDevEnable) {
|
|
aioDebugUpdataFlag |= aobMuteFlag;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int alsaMixerGetMute(AIO_MIXER_S *mixer, int playFlag, int *pVolVal)
|
|
{
|
|
int err = 0;
|
|
|
|
if (mixer->handle == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
snd_mixer_elem_t *elem;
|
|
for (elem = snd_mixer_first_elem(mixer->handle); elem; elem = snd_mixer_elem_next(elem)) {
|
|
const char *elem_name = snd_mixer_selem_get_name(elem);
|
|
if (playFlag && !strcmp(elem_name, AUDIO_PA_SWITCH)) {
|
|
err = snd_mixer_selem_get_playback_switch(elem, 0, pVolVal);
|
|
alogd("get master-volume (0-mute; 1-unmute) switch state: %d", *pVolVal);
|
|
aobMute = (*pVolVal==0?1:0);
|
|
if (aoDevEnable) {
|
|
aioDebugUpdataFlag |= aobMuteFlag;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int alsaMixerSetPlayBackPA(AIO_MIXER_S *mixer, int bHighLevel)
|
|
{
|
|
int err = 0;
|
|
|
|
if (mixer->handle == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
snd_mixer_elem_t *elem;
|
|
for (elem = snd_mixer_first_elem(mixer->handle); elem; elem = snd_mixer_elem_next(elem))
|
|
{
|
|
const char *elem_name = snd_mixer_selem_get_name(elem);
|
|
if (!strcmp(elem_name, AUDIO_PA_SWITCH))
|
|
{
|
|
int ival;
|
|
bHighLevel = bHighLevel?1:0;
|
|
if(snd_mixer_selem_has_playback_switch(elem))
|
|
{
|
|
snd_mixer_selem_get_playback_switch(elem, 0, &ival);
|
|
if (snd_mixer_selem_set_playback_switch(elem, 0, bHighLevel) >= 0)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
aloge("fatal error! set value of playback switch control of a mixer simple element fail[%d]!", err);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
aloge("fatal error! playback switch control is not present!");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int alsaMixerGetPlayBackPA(AIO_MIXER_S *mixer, int *pbHighLevel)
|
|
{
|
|
int err = 0;
|
|
|
|
if (mixer->handle == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
snd_mixer_elem_t *elem;
|
|
for (elem = snd_mixer_first_elem(mixer->handle); elem; elem = snd_mixer_elem_next(elem))
|
|
{
|
|
const char *elem_name = snd_mixer_selem_get_name(elem);
|
|
if (!strcmp(elem_name, AUDIO_PA_SWITCH))
|
|
{
|
|
err = snd_mixer_selem_get_playback_switch(elem, 0, pbHighLevel);
|
|
if(err!=0)
|
|
{
|
|
aloge("fatal error! get player pa wrong[0x%x]", err);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|