2

修复 Linux 中的一个噪音问题

 3 years ago
source link: https://cyril3.github.io/2020/05/17/fix-linux-popup-noise
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

修复 Linux 中的一个噪音问题

2020年05月17日

  自从安装了 Kubuntu 20.04 后,我被一个噪音问题困扰了很久。系统在播放声音前,总会发出一两声短促的噪音。然后继续正常播放声音。后来经过一些研究,我在不久前解决了这个问题。

  刚安装完操作系统后,我安装了媒体播放器,体验音乐和电影的播放效果。在操作过程中,我注意到系统偶尔会发出的短促噪音。在网上搜索一阵,并且尝试了一些方法后,问题没有得到解决。我便放弃了,只当是驱动程序可能有缺陷。

  不过我渐渐地发现了一个规律:噪音只会在一段安静时间后,再次播放声音前产生。在持续播放声音过程中,从来没有噪音。这让我猜测到了一种可能的原因:系统在播放声音一段时间后可能会关闭音频设备,然后在下一次播放前再打开音频设备。打开音频设备的过程中有可能产生电信号的突变,继而产生短促的噪音。这种设计极有可能是基于省电的考虑。

  于是,我以“Linux save power audio noise”为关键词搜索了一番。在 Redhat 公开的 BUG 管理系统中,我找到了2017年报告的一个 BUG:《Bug 1525104 - Clicking noise when start or stop sound playing》

  这个 BUG 所描述的问题和我的相同,并且 Jeremy 似乎给出了解决问题的办法。

Jeremy Cline 2017-12-13 18:06:49 UTC

 Can you pass snd_hda_intel.power_save=0 on the kernel command line and see if that resolves your issue?

 Thanks

Riku Seppala 2017-12-13 18:25:50 UTC

 Yes, that seems to resolve this for me.

  这个方案虽然可行,但需要通过修改 grub 的配置来修改内核命令行参数。即有风险又麻烦,普通用户不好操作。我决定找一个更容易操作的方法。

  “snd_hda_intel.power_save=0”看起来是把“snd_hda_intel”内核模块的“power_save”参数设置为0。那么问题应该是出在这个内核模块上了。于是我下载了 ubuntu 的内核源码,找到了这个内核模块。经过一番搜索,找到了这部分代码(linux-source-5.4.0/sound/pci/hda/hda_intel.c):

#ifdef CONFIG_PM
static int param_set_xint(const char *val, const struct kernel_param *kp);
static const struct kernel_param_ops param_ops_xint = {
	.set = param_set_xint,
	.get = param_get_int,
};
#define param_check_xint param_check_int

static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
module_param(power_save, xint, 0644);
MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
		 "(in second, 0 = disable).");

static bool pm_blacklist = true;
module_param(pm_blacklist, bool, 0644);
MODULE_PARM_DESC(pm_blacklist, "Enable power-management blacklist");

/* reset the HD-audio controller in power save mode.
 * this may give more power-saving, but will take longer time to
 * wake up.
 */
static bool power_save_controller = 1;
module_param(power_save_controller, bool, 0644);
MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode.");
#else
#define power_save	0
#endif /* CONFIG_PM */

  阅读这段代码后,我发现 power_save 参数并非简单地开启或者关闭省电模式,而是一个超时时间。增大超时时间可以降低进入省电模式的频率,从而减少噪音产生的次数。设置0可以直接关闭省电模式。于是问题就变得简单了,我的台式电脑似乎并不那么在乎功耗,所以我决定关闭音频设备的省电模式。用这条命令可暂时关闭省电模式。系统重启后失效。

$ sudo echo 0 > /sys/module/snd_hda_intel/parameters/power_save

  若要永久关闭设备的省电模式,在/etc/modprobe.d/中添加一个“disable_snd_hda_intel_power_save.conf”文件,在文件中添加这一行:

options snd_hda_intel power_save=0

一个更深入的问题

  为什么 Linux 中有这个问题,而 Windows 操作系统中没有?

  Linux 的文档中有一个章节:More Notes on HD-Audio Driver。这一节中介绍了 HD-audio 设备的大致结构。这里引用其中一段:

The HD-audio component consists of two parts: the controller chip and the codec chips on the HD-audio bus. Linux provides a single driver for all controllers, snd-hda-intel. Although the driver name contains a word of a well-known hardware vendor, it’s not specific to it but for all controller chips by other companies. Since the HD-audio controllers are supposed to be compatible, the single snd-hda-driver should work in most cases. But, not surprisingly, there are known bugs and issues specific to each controller type. The snd-hda-intel driver has a bunch of workarounds for these as described below.

  这里说:HD-audio 组件由控制器芯片和解码芯片组成。Linux 提供了控制器芯片的驱动程序。在 snd_hda_intel 模块中,我找到了解码芯片的省电模式的驱动代码(linux-source-5.4.0/sound/pci/hda/hda_codec.c):

static void codec_set_power_save(struct hda_codec *codec, int delay)
{
	struct device *dev = hda_codec_dev(codec);

	if (delay == 0 && codec->auto_runtime_pm)
		delay = 3000;

	if (delay > 0) {
		pm_runtime_set_autosuspend_delay(dev, delay);
		pm_runtime_use_autosuspend(dev);
		pm_runtime_allow(dev);
		if (!pm_runtime_suspended(dev))
			pm_runtime_mark_last_busy(dev);
	} else {
		pm_runtime_dont_use_autosuspend(dev);
		pm_runtime_forbid(dev);
	}
}

  原来音频设备的低功耗是通过控制解码芯片来实现的。于是我猜测电脑主板上应该有这样一个解码芯片:在省电模式中断电,在工作模式中供电。

  我使用的是华硕 B85M-F 型号的主板,在华硕的官网中搜到了此主板的规格说明。此主板搭载了 Realtek® ALC887 8-Channel High Definition Audio CODEC 音频解码芯片。我又在网上找到了这个芯片的数据手册,里面有它的长相和引脚定义。它长这个样子:

debugger

  我在电脑主板上找到了这个芯片,然后把 snd_hda_intel 模块的省电模式打开,用万用表在它的供电引脚上量到了电平的变化。正如我之前猜测的一样:播放声音一段时间后,停止供电。播放声音前,恢复供电。

  最后,我抱着试试看的态度,换了一块硬盘,装上 Windows 10。经过半个多小时的测试,这个音频解码芯片的供电始终没有被切断。看来,Windows下的驱动并没有在低功耗方面理会此芯片。

  到此,这个问题不再扑朔迷离。然而这个问题引发了我更深地思考:当此问题初现的时候,我的第一印象是 Linux 在这些细节上处理的还不够成熟。然而经过以上研究过程,我认为是 Linux 的开发人员追求极致的风格导致了这些琐碎的问题。上文提及的2017年报告的 BUG,到现在依然被开发者们追踪并修改,我为这些开发者们的精神所折服。

——2020年5月17日


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK