音频常见问题总结

时间:2024-04-14 09:44:35

音频文件在播放时出现断断续续或类似“**”(Pop-Click)杂音的现象,称之为 Xrun(可以是 underrun,也可以是 overrun)。

【原因】

    通常来说,出现 Xrun 问题时原因可能是以下几个之一:

    (1) Linux CFS 调度器导致。因为 CFS 调度器的“公平调度”是较长一段时间的平均表现,在很短的一个窗口时间段内,CFS 也可能会将 CPU 时间片完全分配给一个 nice 值更高的线程而不顾及另一个 nice 值更低的线程。如果这个低 nice 值的线程恰好是音频相关的,就会导致 Xrun 问题。

    (2) 更高优先级的 SCHED_FIFO 线程调度。除了音频线程以外,其它线程也可以使用 SCHED_FIFO 标记,如果这个其它线程的优先级高于音频线程,那么它会被优先调度。这样也会导致 Xrun 问题。

    (3) 优先级反转。所谓的优先级反转是指一个更高优先级的线程需要使用另一个更低优先级线程所持有的资源,而不得不等待低优先级线程在使用完毕后将资源释放掉。如果音频线程恰好是这个高优先级线程,此时也将导致 Xrun 问题。

    (4) 过长的调度延时。

    (5) 顶半部中断处理程序执行时间过长。

    (6) 禁用中断时间过长。

    (7) 电源管理。内核会对芯片的工作电源进行适时地控制以免芯片温度过高而烧毁,这些管理策略可能会暂时挂起芯片中正在进行的工作。如果音频相关的任务因此被挂起,那么就会出现 Xrun 问题。

    (8) 内核安全策略原因。

 

【解决办法】

    最好的解决办法当然是分析出 Xrun 的问题到底是上面哪个原因导致的,然后对症下药的改动相应的代码以修复问题。但并不是所有人都对内核各部分了如指掌能精确定位到问题代码的位置(我承认至少我还没有那个水平),所以这里有一个类似万金油的办法:增加音频数据的 buffer 大小或 buffer 数量来进行补偿。但这样也会使音频播放/录音的 数据准备时间变长,也就是说会增加音频操作的延迟,算是这种方法的一个副作用。

 

 

 

插入耳机开机随机出现POP音

分析思路:通过音量渐进设置来播放开机铃声,达到消除POP音

执行步骤:先静音-->开始播放开机铃声-->延迟200ms-->设置开机铃声的音量

结论:此方法也不能很好的消除POP音问题。最后做了一个极端实验:把开机铃声静音后,插入耳机测试发现也会出现随机性的POP音。

由此得出:应该是耳机音频通路上电、打开的时候产生了POP音。由于耳机PA位于耳机音频通路的末端,最后上电、打开。所以确定耳机PA上电时序异常导致此POP音产生。

 

解决方案:

耳机PA上电代码定位:AudioMachineDevice.cpp文件的AnalogOpen()函数。

case AudioAnalogType::DEVICE_OUT_HEADSETR:

case AudioAnalogType::DEVICE_OUT_HEADSETL:

            …….

           mAudioAnalogReg->SetAnalogReg(0x0706, 0x0082, 0xffff); // short HS tovcm and HS output stability EnhanceParasNum

           mAudioAnalogReg->SetAnalogReg(0x0702, 0x0009, 0xffff); // open andheadset power on

            usleep(1000);

           mAudioAnalogReg->SetAnalogReg(0x0704, 0x0940, 0xffff); // not shortHP to vcm

            mAudioAnalogReg->SetAnalogReg(0x0702, 0x000F, 0xffff); //HP power on

            usleep(1000);

           mAudioAnalogReg->SetAnalogReg(0x0704, 0x0100, 0xffff); // HP enhance

            ……

耳机PA上电代码见红色部分。

 

解决方法:

从示波器看,此POP音的持续时间是25ms左右,所以代码将延迟30ms再上电、打开耳机PA。

代码修改如下所示:

……

    usleep(30*1000); // add by baibo cancel headsetpop noise when phone with headset boot

    mAudioAnalogReg->SetAnalogReg(0x0702,0x000F, 0xffff); // HP power on

……

结论:通过延迟打开耳机PA消除此POP音。经过压力测试,此问题没有再复现。

 

 

 

 

 

 POP声是指音频器件在上电、断电瞬间以及上电稳定后,各种操作带来的瞬态冲击所产生的**声。

   下文结合自身在推广小功率D类音频功放IC的过程中遇到的设计问题,就音频系统开关机POP声的解决思路和大家分享一下。

降低或去除POP声方案:

             检查并确认D类功放IC音频输入端电容的值(左右声道各两个)均为104或更小(4个电容值必须相同);

原理说明:

POP声产生的原因与输入电容有关,主要是输入电容充放电造成了POP声,所以输入电容适当减小可以减小POP声。

方法一:D类功放IC关断脚SDL和SDR对GND接电容(见下图)。

             原理说明:

               SDL和SDR为低电平有效,当其处于低电平时电路处于关断状态,所以一上电就使电路处于关断状态就不会产生POP声。原来的电路一上电关断脚是高电平,关断功能失效,所以在该脚对GND接一个22uF左右大电容后,关断脚由于需要对电容充电,所以D类音频功放IC上电以后,关断脚不能马上达到高电平,当电容充电到一定状态后,关断脚达到高电平,D类音频功放IC才能正常工作。所以利用电容充电的时间差,可以避开POP声的产生。电容值可以根据实际情况进行调节。

音频常见问题总结

方法二:修改D类音频IC的MUTE端部分外围(具体修改方法见下图中红色圆圈内部分内容)。

原理说明:

原来电路中MUTE脚一上电处于低电平状态,此时MUTE功能失效,D类音频功放IC可以正常工作,对电路进行修改在MUTE控制端串入电阻6R14,对VCC接4.7uF左右电容后,电路上电后MUTE端电容进行充电,所以该脚上电后先是高电平,接着6R14进行放电,MUTE端电压值慢慢降低到低电平。所以使用MUTE与VCC的上电时序也可以避开POP声部分。电容6C23的值可以根据实际情况进行调节,该电容值越大,MUTE与VCC错开的时序越久。

 

音频常见问题总结

 

 

 

 

 

杂音 & pop 音的解决方法

1. 喇叭有严重的“吱吱”破音,绝大多数的原因有可能在于V(out)电压不稳定,所以最好测一下无负载时的输出电压。同时也可以测量 VCC –即boost 的输出/输入电压。正常的VCC   可以通过客户的 反馈电阻和 VCC负载电阻求得。

2、客户有杂音:估计是信号的干扰,一般是电源的干扰因素占最大的比重,建议测量静态电流 IQ  。

3、POP 音:这个POP音说到底是因为codec开始工作时,耳机声道上的直流电平跳变产生的;手机或一般的手持设备上不会有负电压,音源信号必须在一个直流电平上(如1/2VDD上)输出,这样一个从0电平到1/2VDD的直流跳变,通过隔直电容电容后到耳机上必然会产生POP音。说到底还是搁置电容充放电的声音。

4、POP 音的减小方法:

  方法一:一般来说, IC 上、掉电时的 POP 音是由于偏置电压的瞬间跳变引起的。所以要减小 POP 音就必须抑制 IC 的偏执电压Vbias  的瞬变。Layout  上的体现方法就是增大 Vbias   的滤波电容。对于 TPA6132 来说参考设计是 1 uF 但是偏置电容变得过大会导致 IC 的建立时间变长,会让人感觉声音“久久”没有出来。另外电容过大还会导致 THD+N 变差。

  方法二:对于 TPA6132,我们通过改变时序,是在噪声出来之前关掉class , 通过软件修 改 Depop_mute#和Speaker_mute# 的时序,使其提前关闭。

  方法三:电源开启噪声,地平面反弹的噪声(如一些解码芯片的beep pin接地后,由地噪声引起的不受speak off控制的pop声),等等,首要的是要查明来源。再找方法。如找不到或找到后无法解决,那可以试一下串一个电阻,对pop声的幅值进行压制,当然这也减少了耳机的最大音量。

  方法四:还有一种方法:就是预充电的方法,再加上上拉和下拉电阻将电容的直流电压稳定住,可能效果会比较好。当然首先要确认这个直流偏置1/2VDD到底是多高(有些是可以设置的,有些固定),才好设计上下拉电阻的大小,这样调整后,感觉效果还可以,pop noise几乎感觉不到。

  方法五:现在有些CODEC在软件上有一个寄存器可以设置这个直流电平的上升时间,让它在一定时间内跳变到1/2VDD,而不是很陡的一个上升沿,这样可以在一定程度改善这个POP音。

5、开关机的POP 音问题目前是整个音频功放的瓶颈问题,目前最好的一个解决方法是方法二。对于我们的 Demo 来说,例如切换歌曲的时候,可以先按住 EN 给他一个 low 信号,然后等音乐切换结束的时候再放开

6、补充:左右声道都是采用全差分方式输出的话,那是不需要隔直电容,但是耳机就不能够用标准四线耳机插孔,这种全差分的方式,耳机一般都是用的专用输出插头(与调试接口共用)。

7、特别注意 DEMO 的时候要共地,减小 POP 音产生的可能,检测的时候也会有标准。

 

 

 

dump audio数据定位音频问题

android产品开发过程中,经常会遇到一些音频问题,比如杂音、破音。这个时候需要快速定位问题点。

这里介绍一个遇到的案例,简单描述下问题的定位过程。

 

环境:

ubuntu 16.04 + android (amlogic ARM)

问题:

android产品作为被动蓝牙(类似蓝牙音箱),电脑蓝牙链接后播放1KHZ的正弦波音频,出现破音。android本地播放该音频,无破音。

分析:

首先需要确定硬件还是软件的问题,因为涉及硬件的问题可能会block改版和生产,所以非常急迫。这里硬件涉及codec、功放、喇叭。因为破音非常大的可能性是音频截止了。所以分析音频是最直接有效的方法。

 

首先音频源文件是标准的1KHZ正弦波,扫频也不没发现错误点,一个锅先扔了。然后音频通过蓝牙传给了android,那我们抓一下第一手的数据,就是在audio的HAL层dump出数据。美妙的是,amlogic、高通等的代码都有现成的,放开调试就可以生成想要的文件信息。

/hardware/amlogic/audio/audio_hw.c

static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,                                                                                                                  
                         size_t bytes)
{        
    int ret = 0;
    size_t oldBytes = bytes;
    struct aml_stream_out *out = (struct aml_stream_out *)stream;
    struct aml_audio_device *adev = out->dev;
    size_t frame_size = audio_stream_out_frame_size(stream);
    size_t in_frames = bytes / frame_size;
    size_t out_frames;
    bool force_input_standby = false;
    int16_t *in_buffer = (int16_t *)buffer;
    struct aml_stream_in *in;
    char output_buffer_bytes[RESAMPLER_BUFFER_SIZE + 128];
    uint ouput_len;
    char *data,  *data_dst;
    volatile char *data_src;
    uint i, total_len;
    int codec_type = 0;
    int samesource_flag = 0;
    uint32_t latency_frames = 0;
    int need_mix = 0;
    short *mix_buf = NULL;
    unsigned char enable_dump = getprop_bool("media.audiohal.outdump");
…………
…………
…………
#if 1    
    if (enable_dump && out->hw_sync_mode == 0) {
        FILE *fp1 = fopen("/data/tmp/i2s_audio_out.pcm", "a+");
        if (fp1) {
            int flen = fwrite((char *)buffer, 1, bytes, fp1);
            fclose(fp1);
        }
    }    
#endif 
…………
…………
}

很明显,out_write函数中,只要打开enable_dump就会把音频dump到/data/tmp/i2s_audio_out.pcm中。

enable_dump是由“media.audiohal.outdump”控制,getprop你会发现这个应该是空的。只需要setprop media.audiohal.outdump 1 就可开始抓音频。

抓下音频,adb pull导入到电脑中,用万能的audacity进行分析:

这里可以看到,音频并没有破音,而且丢帧了。通过频谱分析看异常频点会更加清晰。

可以发现,这样的丢帧还是比较多的。

所以这里可以排除硬件的问题了,问题定位到蓝牙丢帧上。

蓝牙的丢帧,可以继续跟踪它的overrun和underrun,后续文章会有详细过程。

 

 

关于android的audiotrack播放声音断断续续的问题

int bufsize = AudioTrack.getMinBufferSize(22050, AudioFormat.CHANNEL_CONFIGURATION_MONO,

AudioFormat.ENCODING_PCM_16BIT);

audioplayer = new AudioTrack(AudioManager.STREAM_MUSIC, 22050, AudioFormat.CHANNEL_CONFIGURATION_MONO,

AudioFormat.ENCODING_PCM_16BIT, bufsize, AudioTrack.MODE_STREAM);

主要是因为buffer设置的太小了,将其改为:

AudioFormat.CHANNEL_CONFIGURATION_MONO,

AudioFormat.ENCODING_PCM_16BIT, bufsize*4, AudioTrack.MODE_STREAM);     即可