编注:

本文是少数派 2022 年度征文活动的入围文章。本文仅代表作者本人观点,少数派对标题和排版略作调整。

今年我们采用了更加依赖用户反馈数据的奖金结算机制,充电、收藏和阅读量都将不同程度地影响文章的最终排名与稿酬倍率。如果你喜欢这篇文章或内容对你有所帮助,请尽量通过充电、收藏或评论等方式表达你的支持与赞赏。


「效率工具」在少数派上简直已经是一个老生常谈的话题了。从日历到笔记,生活中可以用到的几乎任何软件,现在都有无数开发者试图让它们更快、更省力、更强大。另一方面,作为消费者,我们也时时会陷入「效率」的迷宫——换另一个同类的产品是不是真的能让我的工作做得更好?我需不需要为了几个看起来很有吸引力的功能花时间迁移到另一个产品?

这些问题和迷思恐怕在短时间内并不会有什么答案,无论是对于软件市场还是对于我个人。然而就在最近,我逐渐开始意识到,工作和生活中所用到的工具固然重要,但效率的含义远不止于此。我们在生活的许多方面中的努力,实际上都是提升效率的宝贵经验。现代人在面对一个任务的时候,往往会想要去找一个现成的工具,开箱即用

这系列思绪的起点是音乐。从我对数码产品、软硬件还全无概念的时候,我就已经开始在新华书店买 CD 听了。音乐早已成为我生活中无法剥离的一部分,但我并未从事过与之相关的工作,自然也从未想过在其中追求什么效率。不过,经验往往和我们是否与某事谋生无关,而更多的来源于投入其中的时间和精力,就像做菜久了的人哪怕不是专业厨师多少也会对厨艺有些自己的心得。这些年来,我自己在不断消费、了解音乐的过程中,也有意无意地积累了一些技巧,甚至拼凑了一些小工具。从业者或许会嗤之为小打小闹,不过对于我自己来说确实是一种切实的生活品质(Quality of Life)的提升。对于我自己来说如此,相信对于广大同好来说也是如此,因此我想借本文讲讲我对音乐收听、收藏 、整理的一些小技巧,既是分享也是交流。

利用 ffmpeg 实现快捷音频格式转换

但凡对音视频制作略有涉猎的人,对于 ffmpeg 的大名应该都不会陌生。这个始于 2000 年的开源项目如今已经是音视频领域必不可少的基础部件,无论是播放还是处理,几乎所有相关软件和工作流都会或多或少用到它。

在本文的范畴中,我使用 ffmpeg 最频繁的场景是音乐和音频的格式转换。无论是 CD、唱片等实体音乐的抓轨还是网上直接购买的数字版音乐,我几乎每周都会「收获」一些新的、格式各式各样的数字音乐文件,需要经过整理和转码才能放入自己的收藏。

无论是 CD、黑胶、磁带等模拟介质的数字化,还是 Bandcamp 等数字音乐销售平台直接出售的的数字音乐,现在大多数都是以 wav、flac 等无损格式为载体的。虽然爱好者群体里一直对于无损格式在音质上的优势到底是否有意义(以及不同规格的无损音频之间的音质优劣)有不同的看法,但我觉得有一个优势是显而易见的:目前常见的大多数无损音频格式是在使用 PCM(脉冲编码调制)方式进行数字化的音频信号基础上进行不同程度、不同方式的压缩,因此解压缩之后可以获得等价的音频信号。换句话说,目前大多数主流的无损音频格式之间都可以任意互相转换而不损失任何信息。这是 mp3 等有损压缩音频格式无法做到的。对于有损压缩音频格式来说,任何一次有损格式间的转换都需要重新转码,也就是重新进行一次有损压缩,无论如何设置参数,这个过程都会不可逆的损失一部分音频信息。

上文提到的「大多数无损音频格式」也有例外,就是 DSD 格式。DSD 是一种由索尼和飞利浦开发的较新的编码技术,被用于 SACD 等高清音频介质。DSD 采取了和 PCM 不同的形式来编码声音信号,因此传统的基于 PCM 的音频文件和 DSD 音频文件(.dsf、.dff 等)无法无损地互相转换。虽然许多人认为 DSD 在音质上是比 PCM 更优的选择,但目前以该格式发行的录音制品仍然非常少,即使在音乐爱好者的领域也远未达到「常见」的程度,因此不在本文的讨论范围内。

总而言之,一般来说对于自己获取的数字音乐,我做的第一件事就是转码——将所有的无损音乐转换为统一的格式以便于收听。我自己目前在用 Music.app(也就是过去的 iTunes)整理音乐,因此我用的统一格式是 Apple Lossless(.alac)。关于我是如何使用 Music.app 管理我的音乐的,我在过去的文章《用 iTunes 加个人云盘,打造你的专属音乐流媒体服务》《逆「流」而动——如何建立你的数字音乐收藏》中也有提及。

如果是有损格式,因为常见的 .mp3.m4a 两种 Music.app 都可以支持,因此我通常都维持原状以最小化信息的丢失。

转码这件事有非常多的方法可以做。专门的软件如 XLDPermute 都已经非常成熟,常见的数字音频工作站(DAW)软件也可以输出不同格式的音频,不过几乎天天要做的事情,每次都要打开一个 app 来执行,时间长了还是显得有些累赘。

在这个基础上如果还能怎么让这件事变得更方便,纯命令行的 ffmpeg 几乎就是唯一的选择了,它几乎可以用一条命令解决任何音视频处理问题,转换个格式简直就是牛刀杀鸡。我只有一些浅薄的命令行使用经验,不过从个人经验角度,命令行工具虽然很不直观,但是在灵活性上的优势却是大大超出桌面软件的。在少数派讨论过的大多数效率软件,从苹果自带的 Automator、快捷指令到 Keyboard Maestro,都支持自动化执行命令行指令,同一个命令也可以利用不同方式「打包」方便使用。因为我日常本来就在用 Keyboard Maestro,它可以自定义按键组合实现自动化功能的逻辑也很符合我的需求,因此我就想要用它来做这个「一键格式转换器」。

具体来看,使用 ffmpeg 来进行简单的格式转换的命令是如下的格式:

ffmpeg -i {input} -acodec {codec} {output} 

其中 {input} 是需要转换的文件名,{codec} 是转码所用的编码器,{output} 是转换后新文件的文件名。编码器可以理解为 ffmpeg 调用的一些更底层的程序,它们负责把实际的声音数据编码成不同格式的音频文件,通常和输出的文件格式一一对应,因此选定了编码器,基本上输出文件的后缀名也就同时确定了。

反过来说,需要转换的文件本身的格式就不那么重要,大多数情况下,只要是 ffmpeg「认识」的格式,它都可以自动识别并使用对应的解码器,不需要手动设置,只要我在执行命令时知道自己在做什么就可以了。

那么,「一键格式转换器」的构想就比较清楚了。

  1. 首先,因为这是我最前期的音频文件处理,因此大多数情况下都发生在 Finder 中,所以我需要通过 Keyboard Maestro 在 Finder 窗口中直接执行 ffmpeg 命令;
  2. ffmpeg 命令的执行对象当然是 Finder 中我选中的文件,选中谁就对谁执行;
  3. 因为会有转换成不同格式的需求,所以要能够指定输出的编码器(以及文件后缀名)。

对于我来说,一般我会有这样几种转换的需求:

  1. 把任意无损格式转换为 .alac,用于导入 Music.app;
  2. 把任意无损格式转换为 .flac,用来分享给使用非 macOS 系统的朋友,或者用于非音乐类的项目(例如播客剪辑);
  3. 把任意格式转换为 .mp3,用于在微信等场合给朋友或客户分享试听的音乐或音频片段。

因此我只需要设置几个和这几种场景对应的选项(也就是一对编码器和后缀名的参数)就可以了,即使未来可能会有转换更多格式的场景,也只需要简单的添加几个选项就可以。

对于选项的输入,下拉菜单或者手动输入应该都是可以接受的。考虑到音频格式一般都只有三四个字符,我觉得直接写可能还更方便一些,因此就直接采取文本输入的形式了。

确定了工作逻辑,就可以一步一步开始搭建 Keyboard Maestro 的宏了。

首先,我设计了一个包含两个输入框的弹窗,在每次转换开始时让我可以直接输入待转换的和要转换成的音频格式,分别用这两个输入内容赋值 inputFormatoutputFormat 两个变量,并把对我来说最常见的情况作为默认值。这个弹窗看起来是这样:

接着,通过 outputFormat,就可以自动匹配需要写在 ffmpeg 命令中的编码器和输出文件后缀名信息:

因为数量不多,为了操作简单,我就直接用 Switch 功能遍历每一种选项了。我用得到的几种音频格式对应的后缀名和编码器分别是:

音频格式后缀名编码器
FLAC.flacflac
Apple Lossless.m4aalac
MP3.mp3libmp3lame

具体的编码器名可能会因为系统、版本不同而有所区别,可以在终端中执行 ffmpeg -codecs 命令查看本机已安装的所有编码器的确切名称。接着,把对应的编码器名称赋值给 acodec 变量,把对应的后缀名赋值给 outputFormat 变量。

从图中可以看出,mp3 相关的设置要更复杂一些。作为有损压缩格式,mp3 格式比有损压缩格式要多一个比特率的参数。比特率代表音频文件中每秒所包含的(数据量),比特率越高,音频文件单位时间所包含的信息就越多,一般来说音质也就越好。无损压缩格式通常不需要顾忌这些,因为它们并不会改变音频文件中的数据量,比特率只由音频内容决定。因此,在处理 mp3 选项时,我自己规定了一个格式,把比特率跟「mp3」字样连在一起写,再通过简单的查找替换把相应的信息交给这两个参数。

比如,如果我想转换一个 320kbps 的 mp3 文件,我可以在选项中写「mp3320」或者「320mp3」,因为我在进行选项判定时用的是「包含(contain)」而不是「等于(is)」,因此这样并不妨碍 Keyboard Maestro 判定这个选项是 mp3;接着我再通过查找替换删去「mp3」这几个字,剩下的部分就是所需的比特率,把这个值赋给 bitrate 变量。最后,把这些信息拼合成 ffmpeg 命令的编码器和比特率部分,就是 libmp3lame -b:a %bitrate%k

一般来说,在进行格式转换时我并不需要进行任何的重命名操作,因此在下一步中我只需要通过单纯的替换后缀名来生成转换后的新文件名。因为这个 Keyboard Mastro 宏会被用来处理不止一个文件,因此之后的几个步骤都需要被包在一个 For 循环中,针对被选中的每一个文件分别执行。

把上面得到的几个变量组合成最终需要执行的 ffmpeg 命令,也就是 ffmpeg -nostdin -i "$KMVAR_musicFile" -acodec $KMVAR_acodec -vn "$KMVAR_outName"。其中,在终端命令中调用 Keyboard Maestro 中创建的变量时需要加上 $KMVAR_ 的前缀。此外,我还额外加了一些参数。因为这条命令并非真的在终端中执行,因此我用 -nostdin 禁用了终端交互(也就是有时候 ffmpeg 会需要你选择是或否的情况),-y 则规定当已经存在和转换后的新文件同名的文件时直接覆盖。如果不想要这种直接覆盖的选项,去掉这个参数即可,ffmpeg 会在同名文件存在时直接报错;-vn 参数代表如果输入的文件是一个视频,则在输出时仅保留音频内容。

需要注意的是,在 Keyboard Maestro 中执行终端命令时并不会导入真正终端中的 PATH 环境变量,因此如果是非系统自带的应用,比如这里的 ffmpeg,就经常会出现找不到的情况。稳妥起见,这里的程序名一律使用完整的路径。因为我的 ffmpeg 是通过 Homebrew 安装的,完整路径是 /usr/local/bin/ffmpeg,如果你不确定,可以通过 whereis ffmpeg 命令找到。

最后,我在每条命令执行完成之后加了一个「%musicFile% job complete」的通知消息,这样就可以大致通过推送通知了解转换的进度了。

使用快捷键触发

Keyboard Maestro 本身支持很多不同的方式来触发宏,包括一些复杂的快捷键(按两次或者三次的按键组合)。我为这个宏设置了两种不同的触发方式。一种是连按两下 ⌥ + I 的键盘触发;另一种是把它放在 Finder 对应的 Macro Palette 中,当 Finder 窗口在前台时,这个宏会显示在一个侧面浮窗中,可以直接用鼠标点击。这两种方法完全等价,我根据自己方便来选择就好。

使用 Macro Palette 点击触发

你可以从 这里 下载我制做好的 Keyboard Maestro 宏直接使用。

利用正则表达式批量删除不需要的文件

与其说是一个单独的场景,这一节说的操作更多的可能算是为了解决上面一节中产生的问题。在完成转换之后,转换前后的文件会同时存在在同一个文件夹中,通常这时候我会想要一口气删掉转换前的原始文件。当文件数量比较多的时候,一个个来删除当然显得很愚蠢,特别是这种一组同名但不同后缀名的文件排列在一起的时候经常还是间隔显示的,很难直接批量选中。

这件事情在终端中做实际上就相当容易,只要执行一条 rm *.flacrm *.mp3 这样的命令就可以了。作为一个大多数时间还是在 GUI 中活动的人,我还是想把类似的操作搬到 Finder 中。

好在 Keyboard Maestro 完全可以做到这个操作,甚至不需要使用终端命令。这个场景中我的需求大概是:在 Finder 中选中任意数量的文件,触发 Keyboard Mastro 宏并输入正则表达式(例如 *.flac),删除被表达式匹配到的文件

第一步自然是输入正则表达式,使用默认的弹窗组件即可完成。输入的内容保存在 keyWord 变量中。

Keyboard Maestro 支持直接获取 Finder 中选中的内容,用一个 For Each 循环对每个选中的文件进行匹配即可。因为我想要输入的内容本身就已经是正则表达式了,因此直接把输入的内容作为匹配的指令填入判断组件即可(即图中的「matches」处)。注意在这里的开头要加上一个 ^ 字符表示字符串的起始,否则文件名可能无法正确匹配。假如文件名符合正则表达式,则执行删除。为了避免误触,这个宏我没有给它绑定快捷键,而是和上一节中的音频转换器一样放在了 Macro Palette 浮窗中,有时候我也会借助 Raycast 来触发。

虽然实际上是非常简单的一个操作,不过这个宏实际上是我使用频率最高的。除了批量删除音频文件转换的副产物,我也经常会在别的时候用到它,也为我节省了不少时间。

你可以从 这里 下载我制做好的 Keyboard Maestro 宏直接使用。

利用 Mediainfo 实现快速查看音频文件信息

除了对音频文件作各种处理,查看音频文件的属性也是很常出现的场景。除了音乐,我现在在做涉及播客的工作中也要和各种各样的录音文件和声音素材打交道。

一般来说,「查看音频文件信息」会对应两件不同的事:查看文件的元数据信息,或音频的规格。前者指的是记录在音频文件中的标准化文本标签,例如曲目名、表演者名等,对于 mp3 来说这些信息会以 ID3 格式储存在音频文件中,对于 FLAC 和 ogg 等则是 Vorbis Comment 格式;后者指的是音频文件本身的数据信息,例如比特率、位深、采样率等。

电脑对于音频文件的支持历史悠久,即使是资源管理器或者 Finder 本身也或多或少能显示一些这类信息,但普遍不全面。例如下图中是 macOS 和 Windows 上资源管理器对同一个 mp3 文件的显示,可以看出,无论是 ID3 还是文件的规格,都可以在文件预览窗口和属性详情中看到一些。但也可以看到明显的缺失,例如 macOS 的详情中显示了文件的采样率(48kHZ),但是并没有比特率和位深;而 Windows 的资源管理器显示了比特率(286kbps),但却没有采样率和位深。

Finder 对 mp3 文件的元数据和技术信息的显示
Windows Explorer 对 mp3 文件的元数据和技术信息的显示

因此,仅仅依赖系统的自带是不够的。另一个方法则是借助音频播放软件。目前市面上大多数音乐播放器和元数据编辑软件都可以完整的显示这些信息,但对于「快速查看」这个需求来说,这样操作未免略显有些麻烦,特别是当要一口气查看比照很多不同的文件的信息时,不说操作效率,用同一个软件批量打开很多文件,性能都可能会成问题。

而这个问题的「终极方案」是 Mediainfo。Mediainfo 最早发布于 2002 年,迄今为止唯一的功能就是显示媒体文件的技术信息和标签信息。所谓的媒体文件不仅指音频文件,也包括视频、图片、字幕和许多其他类型的文件。虽然只有一个功能,但 Mediainfo 把这个功能做到了完善得夸张的程度,我没有见过任何它不支持的音视频文件,哪怕你扔给它一个 exe 文件,它都会努力告诉你这个可执行文件适用于哪款处理器指令集。

大多数情况下,用户都会直接使用 mediainfo 的命令行版本,只要输入 mediainfo 文件名 一行命令,终端就会返回完善的媒体文件信息。

不过对我来说,这又回到了前面那个问题——我大多数时候并不在终端里工作,所以每次要查信息都要打开终端有些太麻烦了。虽然 Mediainfo 官方提供了同样 全面到夸张的 GUI 选项(甚至还包括 一个网页版),但是单独打开一个制作颇为简陋,无法串到自动化流程里的图形软件的麻烦程度和终端也不相上下。

这个时候 Keyboard Maestro 似乎又可以派上用场了。从前面的命令行执行结果可以看出,Mediainfo 的输出形式是简单的多行纯文本,而用 Keyboard Maestro 执行终端命令时也提供将终端的输出结果以弹窗形式显示的选项,这样一结合,不就跟一个 GUI 程序没什么两样了?在呼出方面甚至还要更方便一些。

这一个 Keyboard Maestro 宏只需要两步:获取包含完整路径的文件名,执行终端命令并以弹窗形式显示输出信息。考虑到可能会有批量执行的情况,我把这两个步骤也包在了一个 For Each 循环里。

执行效果,非常靠谱

你可以从 这里 下载我制做好的 Keyboard Maestro 宏直接使用。

利用 spsox 批量生成音频频谱图

在遇到一些来源可疑的音乐时,有时候会遇到「假无损」的情况,也就是看起来是无损格式的音频文件,实际上是由有损压缩的音频文件二次转码而成的。在这种情况中,不但音频的信息量完全没有增加,还徒增了不少文件体积。这种情况不但出现在盗版音乐中,实际上在正版音乐中也不少见,例如一些音乐人在创作过程中使用了有损压缩过的采样素材(这并不是什么问题),或者一些来自相对欠发达地区的音乐发行并未采取很高的工业标准,都可能造成类似的结果。

无论原因为何,有时候确实需要辨别。最方便也最可操作的方法是用眼睛通过频谱辨别。mp3 等有损压缩格式会切掉音乐中理论上人耳不可闻(或不太可闻,取决于压缩程度)的部分,虽然不可闻,但这样的处理在视觉化的声音频谱上是清晰可见的。

图:https://uvicaudio.wordpress.com/2015/01/24/lossy-formats-2/

从上图可以看出,mp3 文件的高频部分有一条非常明显的近乎水平的削减,而 CD 中的无损音频的高频部分则是平滑的。

要生成这样的频谱图通常需要借助数字音频工作站(DAW)软件。DAW 软件通常都具有全功能的音频编辑能力,当然也可以用来做频谱图。但是仅仅用来目辨音频,就有些小题大做了。不仅如此,DAW 软件也恰恰因为功能过于全面,用来做这样的小事反而不太方便,你可能需要点击好几个菜单,拖拽好几次窗口,才能获得比较理想的频谱显示。

那么有没有更加简单直接的方法呢?答案又是命令行。SoX 最初发布于 1991 年,是比 ffmpeg 还老的活化石级音频编辑软件了。相对来说它更专精于音频,提供一些类似降噪、低通滤波、音量调整等更精细的操作,频谱分析也在其中。

使用 SoX 生成频谱图同样只需要一条命令,格式大概是 sox xxx.flac -n spectrogram -o xxx.png。也可以使用各种参数指定生成图片的尺寸,以及具体用哪一部分的音频来生成频谱图。

不过,这个工作我没有自己从头开始折腾 SoX 的参数,而是使用了网友制作的一个现成的脚本 spsox。简单地说,这个脚本会为指定文件夹中的所有 flac 文件生成两张频谱图,一张包括了整个音轨(.full 后缀),另一张则是截取其中两秒并放大的局部(.zoomed 后缀,为了避免截取到一些音乐开头的空白部分,spsox 使用的是每个音轨的 1:00-1:02 这一段),并将所有的图放入 specs 子文件夹中。

虽然功能比较简陋,这个脚本倒是足以满足我的需求了。我的构想大致如下:在 Finder 中选中某个文件夹,则对文件夹中的所有 flac 文件生成频谱图;在 Finder 中选中某个文件,则对这个文件所在文件夹中的所有 flac 文件生成频谱图

这样以来我自己的操作就可以比较有灵活性,无论在文件夹上还是文件夹里,都可以直接呼出 spsox。

首先,要在系统中安装 SoX 和 spsox。macOS 可以直接通过 Homebrew 安装。spsox 则需要从 其 Github 页面 下载,用 chmod +x spsox 命令给脚本可执行权限,并将其安装到 /usr/local/bin/ 目录中。

接着,我还是使用 Keyboard Maestro 来实现脚本的执行。首先,使用 Get File Type 模块来判断 Finder 中当前选中的是目录还是文件。

接着是一个简单的 if 判定,如果选中的是文件夹,则直接把文件夹的完整目录赋值给 pathStr 变量;如果选中的不是文件夹,则使用 dirname 命令获取文件的所在目录并赋值给 pathStr 变量。

接着,通过赋值 ENV_PWD 变量,把 pathStr 设置为 Keyboard Maestro 下一步中执行终端命令的工作目录。最后直接执行 spsox 命令即可。

这个宏因为使用的次数相对较少,对便捷性也没有那么高的要求,所以我也没有给它绑定快捷键,而是单纯从 Macro Palette 浮窗鼠标点击触发。考虑到我几乎不会关闭 Keyboard Maestro,这种即点即用的体验也足够方便了。

你可以从 这里 下载我制作好的 Keyboard Maestro 宏直接使用。

尾声

以上就是我在听音乐、和数字音频打交道的过程中总结的一些经验和它们的产物,虽然仍然相当简陋,十有八九也并非任何情况下的最优解,但它们是其中足够成熟,以至于我认为可以与大家分享的部分。虽然我自己使用的都是 Keyboard Maestro 实现,但上面的每一个应用都不涉及复杂的、专有的步骤,稍加改动就可以移植到其它的自动化工具上。这些功能本身虽然未必所有人都需要,但也希望中间思考和搭建的过程可以给诸位读者些许启发。

如今各种工具一天比一天光鲜和智能,这当然是好事,但每个人可能多少都会遇到一些时候,或许因为需求特殊,或许因为领域冷门,而没有办法被这些工具所满足。这样的时候止步不前而不停地重复复杂繁琐的方法并不是唯一的路径,我们仍然可以拿起数字世界的滑轮和杠杆,一砖一瓦地建造属于自己的「效率」实践。  

注:题图由 Midjourney 生成。

> 下载 少数派 2.0 客户端、关注 少数派公众号,解锁全新阅读体验 📰

> 实用、好用的 正版软件,少数派为你呈现 🚀