解决Alsa underrun爆音问题的一种措施

音频下溢时候的造成的pop音问题是音频常见的一种问题,如果能找到根本原因避免这种下溢是最好的解决方案,但一个系统任何时候都不会有下溢产生我想也不大可能,系统的瞬时负载过高,或者一些不规范的码流也容易造成一种难以避免的下溢,这时候就经常会遇到pop音问题了。

以下提供一种在codec层优化pop音的方案,当然这个前提是系统硬件使用了数字功放,且是遵循了linux alsa框架的标准驱动。

在codec 驱动注册codec dai接口时,其中的ops的类型是 struct snd_soc_dai_ops,定义了DAI驱动的操作接口

struct snd_soc_dai_ops {
	/*
	 * DAI clocking configuration, all optional.
	 * Called by soc_card drivers, normally in their hw_params.
	 */
	int (*set_sysclk)(struct snd_soc_dai *dai,
		int clk_id, unsigned int freq, int dir);
	int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source,
		unsigned int freq_in, unsigned int freq_out);
	int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
	int (*set_bclk_ratio)(struct snd_soc_dai *dai, unsigned int ratio);

	/*
	 * DAI format configuration
	 * Called by soc_card drivers, normally in their hw_params.
	 */
	int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
	int (*xlate_tdm_slot_mask)(unsigned int slots,
		unsigned int *tx_mask, unsigned int *rx_mask);
	int (*set_tdm_slot)(struct snd_soc_dai *dai,
		unsigned int tx_mask, unsigned int rx_mask,
		int slots, int slot_width);
	int (*set_channel_map)(struct snd_soc_dai *dai,
		unsigned int tx_num, unsigned int *tx_slot,
		unsigned int rx_num, unsigned int *rx_slot);
	int (*get_channel_map)(struct snd_soc_dai *dai,
			unsigned int *tx_num, unsigned int *tx_slot,
			unsigned int *rx_num, unsigned int *rx_slot);
	int (*set_tristate)(struct snd_soc_dai *dai, int tristate);

	int (*set_sdw_stream)(struct snd_soc_dai *dai,
			void *stream, int direction);
	/*
	 * DAI digital mute - optional.
	 * Called by soc-core to minimise any pops.
	 */
	int (*digital_mute)(struct snd_soc_dai *dai, int mute);
	int (*mute_stream)(struct snd_soc_dai *dai, int mute, int stream);

	/*
	 * ALSA PCM audio operations - all optional.
	 * Called by soc-core during audio PCM operations.
	 */
	int (*startup)(struct snd_pcm_substream *,
		struct snd_soc_dai *);
	void (*shutdown)(struct snd_pcm_substream *,
		struct snd_soc_dai *);
	int (*hw_params)(struct snd_pcm_substream *,
		struct snd_pcm_hw_params *, struct snd_soc_dai *);
	int (*hw_free)(struct snd_pcm_substream *,
		struct snd_soc_dai *);
	int (*prepare)(struct snd_pcm_substream *,
		struct snd_soc_dai *);
	/*
	 * NOTE: Commands passed to the trigger function are not necessarily
	 * compatible with the current state of the dai. For example this
	 * sequence of commands is possible: START STOP STOP.
	 * So do not unconditionally use refcounting functions in the trigger
	 * function, e.g. clk_enable/disable.
	 */
	int (*trigger)(struct snd_pcm_substream *, int,
		struct snd_soc_dai *);
	int (*bespoke_trigger)(struct snd_pcm_substream *, int,
		struct snd_soc_dai *);
	/*
	 * For hardware based FIFO caused delay reporting.
	 * Optional.
	 */
	snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,
		struct snd_soc_dai *);
};

这些ops接口中的trigger会由alsa core回调

int (*trigger)(struct snd_pcm_substream *, int, struct snd_soc_dai *);

音频下溢 alsa underun的时候alsa core会回调trigger 触发stop和start的开关动作,我们可以在这里

添加mute、unmute codec的动作以避免下溢的pop音,以tas5805的驱动为例:

static const struct snd_soc_dai_ops tas5805m_dai_ops = {
	.trigger = tas5805m_trigger,
};

static int tas5805m_mute(struct snd_soc_component *component, bool mute)
{
	//struct tas5805m_priv *tas5805m = snd_soc_component_get_drvdata(component);
	u8 reg03_value = 0;
	u8 reg35_value = 0;

	if (mute) {
		//mute both left & right channels
		reg03_value = 0x0b;
		reg35_value = 0x00;
	} else {
		//unmute
		reg03_value = 0x03;
		reg35_value = 0x11;
	}

	snd_soc_component_write(component, TAS5805M_REG_00, TAS5805M_PAGE_00);
	snd_soc_component_write(component, TAS5805M_REG_7F, TAS5805M_BOOK_00);
	snd_soc_component_write(component, TAS5805M_REG_00, TAS5805M_PAGE_00);
	snd_soc_component_write(component, TAS5805M_REG_03, reg03_value);
	snd_soc_component_write(component, TAS5805M_REG_35, reg35_value);
	//tas5805m->mute = mute;

	return 0;
}
static int tas5805m_trigger(struct snd_pcm_substream *substream, int cmd,
			       struct snd_soc_dai *codec_dai)
{
	struct tas5805m_priv *tas5805m = snd_soc_dai_get_drvdata(codec_dai);
	struct snd_soc_component *component = tas5805m->component;

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		switch (cmd) {
		case SNDRV_PCM_TRIGGER_START:
		case SNDRV_PCM_TRIGGER_RESUME:
		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
			if (!tas5805m->mute)
				tas5805m_mute(component, 0);
			break;
		case SNDRV_PCM_TRIGGER_STOP:
		case SNDRV_PCM_TRIGGER_SUSPEND:
		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
			if (!tas5805m->mute)
				tas5805m_mute(component, 1);
			break;
		}
	}
	return 0;
}

其他的数字功放驱动都可以使用这种方式来进行优化。

如果系统使用的模拟功放那么也可以在soc platform层cpu dai以及采用同样的方式,在trigger回调中添加platform提供的mute输出的接口。

注意的是这里的操作是原子的,不能有耗时的操作。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值