1.先要理解两个常量,一个是voccal.c中的常量voc_pcm_on_chip_0_gsm_cal,结构如下:
typedef struct {
voc_ec_type ec_mode; /* Echo Cancellation mode */
voc_ns_type ns_enable; /* Noise suppressor enable */
uint16 tx_gain; /* TX Voice Volume */
uint16 dtmf_tx_gain; /* TX DTMF gain */
uint16 codec_tx_gain; /* CODEC TX gain */
uint16 codec_rx_gain; /* CODEC RX gain */
uint16 codec_st_gain; /* CODEC ST gain */
qdsp_cmd_pcm_filter_type tx_filter; /* TX PCM filter coefficients*/
qdsp_cmd_pcm_filter_type rx_filter; /* RX PCM filter coefficients*/
sint15 rx_dbm_offset; /* RX offset in dBm */
qdsp_cmd_agc_param_type agc_param; /* AGC/AVC parameters */
……
……
}voc_pcm_path_cal_type;
根据注释,很容易找到Tx Codec Gain, Tx Volume, Rx Codec Gain, ST Codec Gain, Tx PCM Filter, Rx PCM Filter。这里找到的Gain和Volume增益计算公式为:Gain(dB) = 20*LOG(Value/16384)。
2.另一个是Sndcal.c中的snd_cal_handset_voice_vol。平台默认为四级音量,数组的第一竖列就是每级音量对应Rx Volume的大小。
VOL_MEMORY snd_gen_level_voc_type snd_cal_handset_voice_vol[] = {
{ VOC_VOL_SILENCE , VOC_VOL_SILENCE , 0 },
{ -1100 , -3400 , 0 },
{ -500 , -3300 , 0 },
{ 100 , -3200 , 0 },
{ 700 , -3100 , 0 }
};
Rx volume计算公式为:Value = 100* Gain(dB)。上例四级音量分别为-11,-5,1,7dB,最大音量7dB。需要说明的是每级音量至少间隔4dB,才能被人耳区分出来。
3. 对照音频的款图可以发现,以下参数可以调节:
Tx Volume ---------voccal.c
Tx Codec Gain ---------voccal.c
Rx Codec Gain ---------voccal.c
ST Codec Gain ---------voccal.c
Tx PCM Filter ---------voccal.c
Rx PCM Filter ---------voccal.c
Rx Volume(max) ---------snddcal.c
做过高通平台的msm7X27,msm7x27a,qrd8x25的audio驱动工作,关于android的audio流程网上可以查到很多内容,就总结下调试audio遇到的问题吧
这是整个audio tx,rx的流程。包括voice的,很清晰
一。关于耳机检测及耳机按键检测的问题
耳机插拔检测是靠一个GPIO管脚检测,有上拉电阻到vbat,没有插耳机为高电平,插入之后变低电平,报中断
对应的gpio配置code在hs_gpio_cfg_s.c, 详见数组hsi_gpio_info
关于OTHC,在qcom网站找到的解释是这样的
The one-touch headset detection feature is supported through the HSET_BIAS pin. The internal hardware in this device performs actions
based on the amount of current being drawn by the headset microphone. The HSET_BIAS output functions like a traditional microphone
bias except that it has some additional features to support one-touch headset detection.
而事实上,这几个平台都不是OTHC的。
msm7X27,msm7x27a平台的耳机按键检测是adc读取值进行判断的,qrd8625我们是利用micbias的电流进行判断的
相关耳机插拔代码及键值上报code见hs_othc.c
二。关于audio部分
1。多媒体音量的调节
如果是提高声音,可以调整index值,降低音量的话,对计算出来的volume进行衰减就可以了
例如:衰减
if((stream == AudioSystem::MUSIC) && (mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET))
{
volume = volume *0.30903;
}
android2.3跟android4.0volume的计算方式有差别,android2.3不管是哪种模式哪种媒体流用的是同一种计算方法,而android4.0有分开处理,默认的情况下,音量的曲线都不是很好,音量低部分变化不明显,可以调整db/step
如在android4.0上
#if 0
const AudioPolicyManagerBase::VolumeCurvePoint
AudioPolicyManagerBase::sDefaultMediaVolumeCurve[AudioPolicyManagerBase::VOLCNT] = {
{1, -58.0f}, {20, -40.0f}, {60, -17.0f}, {100, 0.0f}
};
#else
const AudioPolicyManagerBase::VolumeCurvePoint
AudioPolicyManagerBase::sDefaultMediaVolumeCurve[AudioPolicyManagerBase::VOLCNT] = {
{1, -30.0f}, {20, -24.0f}, {60, -12.0f}, {100, 0.0f}
};
#endif
这是对media音量进行的调整,基本上是均匀变化的。基本思想就是,例如0.0f-(-12.0f)/100-60=(-12.0f)-(-24.0f)/60-20约等于-24.0f-(-30.f)/20-1
关于android音量控制,audioservice.java里定义了每种媒体流的音量等级
For example:
/** @hide Maximum volume index values for audio streams */
/* private int[] MAX_STREAM_VOLUME = new int[] {
7, // STREAM_SYSTEM
7, // STREAM_RING
15, // STREAM_MUSIC
7, // STREAM_ALARM
7, // STREAM_NOTIFICATION
15, // STREAM_BLUETOOTH_SCO
7, // STREAM_SYSTEM_ENFORCED
15, // STREAM_DTMF
15 // STREAM_TTS
};*/
在audiomanager.java里设定每种媒体流默认音量级别
For example
/** @hide Default volume index values for audio streams*/
/* private int[]DEFAULT_STREAM_VALUE = new int[] {
7, // STREAM_SYSTEM
7, // STREAM_RING
15, // STREAM_MUSIC
7, // STREAM_ALARM
7, // STREAM_NOTIFICATION
15, // STREAM_BLUETOOTH_SCO
7, // STREAM_SYSTEM_ENFORCED
15, // STREAM_DTMF
15 // STREAM_TTS
};*/
2. adjust peaker ---ringtone or media
可以调节MBADRC 的band改变频响,这个调节的过程由硬件完成,硬件会给audiofilter.csv文件
The AudioFilter.csv file can be parsed in AudioHardware.cpp. The tuning parameters are pushed down to the ADSP when there is a device change.
1: Speaker,
2: Handset,
3: Headset
3。连接蓝牙耳机,notification会断续,播ringtone的时候,speaker声音会断续,not smoothly
将a2dpUsedForSonification()改为false,声音就不会从蓝牙耳机里播放。产生这种断续现象的原因,在播放 notification or ringtone的时候,蓝牙跟speaker设备会同时打开,即使对于来电铃音来说蓝牙耳机会播放自己的提示音,但code里还是会把这个设备打开,蓝 牙工作的时候需要唤醒,当声音开始播放的时候,蓝牙跟蓝牙耳机的通信不是立刻建立。当播来电铃声的时候,来电铃声的数据会送到蓝牙协议栈,但是这部分数据 却没有送到蓝牙耳机播放,是怎么处理的不清楚,但是会影响到speaker播放ringtone
4. C网来电,拒接,铃声突然变大
The audio is played at 48KHz sample-rate so ringtone is also played 48KHz.
On CDMA call, vocder is acquired during the ringtone playback so ringtone is played at 8KHz sample-rate.
This issue is presented on only CDMA call.
To avoid this issue, need to undifne "FEATURE_RX_VOICE_OVER_HOST_PCM" in custXXXXX.h file(=AMSS side).
5.点击开始录像,开始提示音响后会有兹兹声
playback跟capture动作切换造成
status_t AudioHardware::AudioStreamInMSM72xx::standby()
{
//mHardware->clearCurDevice();
//mHardware->doRouting(this);
mHardware->doRouting(NULL);
}
二。voice部分
1。关于蓝牙打电话
由于qrd8625这个项目外挂了一个modem,着实被蓝牙打电话折腾够呛,初期评估了解的情况不足也是导致调试时间花费比较多的原因
首先高通的默认code是主芯片做pcm的master,提供时钟,而外挂的modem pcm口只能做slave,不能做master,那么就需要将BT PCM做master,这样才行进行通信,简直为难死人。提case给高通,确认BT可以做master后,开始着手调试。看到BT相关文件的那些tag 啊,都要崩溃了。还好经过有一个周吧,把pcm的主时钟给搞出来了,高通主芯片配成slave模式是有文档的,这个可以参照 80_VH558_1_D_AUX_PCM_MSM72xxA_MSM75xxA_MSM76xx_MSM7x27.pdf文档去配。
其实BT做master也很容易配。改的很简单,只是当时tag跟代码里对不上,用的蓝牙芯片是WCN2243
文件vender\qcom\proprietary\bt\hci_qcomm_init\btqsoncnvmtags.c
static bt_qsoc_cfg_cst_element_type bt_qsoc_cfg_tag_44_bha_b0[] = \
{
0x2C, 0x01, 0x2C, 0x29,
0x2F, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,
0xFF, 0x10, 0x02, 0x02, 0x01, 0x00, 0x14, 0x01,
0x06, 0x28, 0xA0, 0x62, 0x03, 0x64, 0x01, 0x01,
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0,
0xFF, 0x10, 0x02, 0x01, 0x00, 0x14, 0x01, 0x02,
0x03
};
要是改成master,需要将0x10改成0x00,这个是配置BT PCM是做master还是slave的寄存器,0x50这个寄存器,0x01代表的时钟是25.6khz,这个详见spec了,然后0x50就是2048khz,这个就是pcm工作的时钟,帧同步信号是0xFF这个寄存器,FF代表了256分频,正好是8k,帧同步信号跟pcm数据的采样率一致。
接着就开始建立通话部分调试,上行有声音,下行没有声音,需要将主芯片的PCM口四根线PCM_CLK,PCM_ASYNC,PCM_IN,PCM_OUT配成高阻态,高阻态的配置只需要配成gpio无拉输入状态即可。
2。蓝牙打电话后手机完全没有声音
EVDO蓝牙打电话后,挂断电话手机完全没有声音,这个问题产生的原因就是BT PCMmaster,主芯片PCM slave造成的,再挂断电话时有个提示音没有播出来。从log看是申请不到内存,其实是能看到obtainBuffer timed out (is the CPU pegged?)这句log,后来高通给了patch,是在audio_out.c文件做的修改
将audio_write()函数中的进程睡眠唤醒方式做了修改
rc = wait_event_interruptible(audio->wait,
(frame->used == 0) || (audio->stopped));
改成
rc = wait_event_interruptible_timeout(audio->wait,
(frame->used == 0) || (audio->stopped),
msecs_to_jiffies(2000));
3.主micbias,hp micbias都是可以在AP侧发rpc到cp请求使能or dis功能
主micbias enable/disable
adie_codec_write (ADIE_CODEC_EN3_R,
ADIE_CODEC_EN3_EN_MICBIAS_M,
ADIE_CODEC_EN3_MICBIAS_ENA_V/ADIE_CODEC_EN3_MICBIAS_DIS_V);
hp micbias,有这样几种状态可以选择
typedef enum
{
/**
* Headset module off
*/
PM_HSED_ENABLE_OFF,
/**
* enabled if TCXO signal is high
*/
PM_HSED_ENABLE_TCXO,
/**
* enabled if PWM or TCXO signal is high
*/
PM_HSED_ENABLE_PWM_TCXO,
/**
* enabled always
*/
PM_HSED_ENABLE_ALWAYS,
/**
* Invalid selection, used for range checking in PMIC APIs
*/
PM_HSED_ENABLE_TYPE__INVALID
}pm_hsed_enable_type;
4。录音声音小的问题
一是提高录的声音,二是提高播的声音,但是调整播放相关的增益会影响其他音频的输出,最好能提高录的声音
在android4.0+qrd8625上可以这样修改
可以增大codectxgain,tx volume
增大codectxgain
1 在vocdata.c
voc_data_codec_gain_ctrl_type* voc_data_get_codec_gain()
{
....
if (voc_check_in_call())
{
voc_data_codec_gain.codec_st_gain = voc_data_pcm_cal_ptr->codec_st_gain;
}
else
{
+ voc_data_codec_gain.codec_tx_gain =设置新的gain
#if defined(T_MSM7x27A) || defined(T_MSM7x25A)
if (voc_data_codec_inuse == VOC_CODEC_STEREO_HEADSET) {
// Hack for power measurement
voc_data_codec_gain.codec_rx_gain = VOC_DATA_UNITY_GAIN;
}
.....
}
增大tx volume
1 在vocproc.c
vocproc_state_return_type vocproc_state_active (void)
{
....
+if(!voc_check_in_call())
+ vocproc_set_volume_normal();
return (VOCPROC_STATE_SUCCESS);
}
2
extern vocproc_status_type vocproc_set_volume()
{
...
if( voc_check_in_call() == FALSE ) {
vocproc_vol.tx_comfort_noise_ena = QDSP_COMFORT_NOISE_DIS_V ;
+ vocproc_vol.tx_vol=设置新的gain
}
}
5。打开PGA通路
早期的FM通路是这样走的,qrd8625平台外挂modem电话免提模式是这样做的
ADIE register configurations are located in ...\services\adsp\msmaud.h
To enable AUX_PGA loopback, you should logical OR the existing register values with the following for the specific audio path that you are working with.
ADIE_CODEC_EN5_R/*0x39*/, MSMAUD_ADIE_CODEC_EN5_ENABLE_BANDGAP_AUXPGA_LR/*0x0C*/, \
ADIE_CODEC_C7B_R/*0x43*/, MSMAUD_ADIE_CODEC_C7B_AUX_PGA_RXP_L_R/*0x2F*/, \
#define MSMAUD_ADIE_CODEC_C7B_AUX_PGA_RXP_L_R \
( MSMAUD_ADIE_CODEC_C7B_INIT | \
ADIE_CODEC_C7B_AUX_PGA_RXP_L_SEL_V | \
ADIE_CODEC_C7B_AUX_PGA_RXP_R_SEL_V | \
ADIE_CODEC_C7B_AUX_PGA_RXM_L_SEL_V | \
ADIE_CODEC_C7B_AUX_PGA_RXM_R_SEL_V | \
ADIE_CODEC_C7B_AUX_PGA_VOCM_LOCAL_V )
ADIE_CODEC_C10_R/*0x46*/, /*must be configured for whatever output you're going through. See the description for CODEC_C10 in the Software Interface Manual for your target*/
ADIE_CODEC_C10_R, (ADIE_CODEC_C10_HPH_L_ADD_LINEI_L_ENA_V \
| ADIE_CODEC_C10_HPH_R_ADD_LINEI_R_ENA_V)
可以参考芯片sepc关于codec寄存器的配置说明