我的“玄学”实践与技术探索

我的“玄学”实践与技术探索

Life always finds a way
—— Dr. Ian Malcolm

第一个问题

自从我开始觉得下班回家后玩游戏变成另一种“加班”,转而掉进了一种“什么也不用干,只需要坐在沙发或躺在床上就能参与”,同时也被很多人称为 玄学 的新坑 —— Hi-Fi 后。我遇到了一些新的问题,也因此产生了一些解决方案。

和最开始的设想(什么也不用干,只需要坐在沙发或躺在床上就能参与)不太一样,在经过了大量研究与学习,并初步弄懂音箱的阻抗(Impedance)和灵敏度(Sensitivity),功放的功率 (Power output) 和阻尼系数 (Damping factor),它们之间的关系,以及什么是前级 (Pre Amplifier) ,后级 (Power Amplifier) ,合并式功放 (Integrated Amplifier) 等等诸多概念后。高高兴兴把一台合并式功放外加一对书架音箱组成的最基本的音响系统抱回家并设置好后,第一个问题就这么自然而然的出现了:现在要怎么播放我想要听的音乐?

HEGEL H190v 正面
HEGEL H190v 背面

这台 HEGEL H190v 合并式功放通过网线接上家里的局域网后就可以支持 AirPlay, Roon Ready, Spotify Connect (Not Lossless), UPnP, JPLAY Certified 这些串流协议。

作为一个 Apple One 订阅用户,自然首先就注意到了 AirPlay 。只不过和之前从自家那台 Bose Smart Soundbar 900 得到的 AirPlay 体验一样,它不太稳定。偶尔在播放器列表里面会找不到这个播放器,只能通过手动切断设备的电源再重新通电才能恢复,不胜其烦。

而且 Apple Music 里面的 “高解析度无损 ALAC 24bit/192kHz“ 音乐在通过 AirPlay 播放时,会出现与预期不符的现象。当它通过 AirPlay 1 协议播放时,最高只能到 “无损 ALAC 24bit/48kHz”,如果源文件超过此范围,系统会自动将其下采样至最高
支持的 48kHz 再进行传输。而通过新的 AirPlay 2 协议播放时,事情就变得不稳定起来,特别是在非苹果设备上播放时,在许多第三方 AirPlay 2 设备上,用户发现播放 Apple Music 时并不能稳定获得无损输出,部分情况下实际传输的是 AAC 编码的数据,具体行为由于 Apple 并未公开协议细节,也因厂商实现不同而存在差异。这也是很多 Hi-Fi 厂商的产品只支持 AirPlay 1 的原因。至少能够稳定地进行无损 PCM/ALAC 传输(通常最高 24bit/48kHz)。以至于 玄学 Hi-Fi界所推崇的“比特完美(bit-perfect)”更是无从谈起。

鉴于此,我得出一个结论。要想完美的使用 Apple Music 就得找一台能运行 Apple Music 客户端的电脑,然后物理连接到功放上。再通过这台电脑来播放。如此一来,‘如何播放’ 的问题看似解决了,却衍生出了 ‘如何远程操控’ 的新问题——而这恰恰是 AirPlay 的核心功能。本质上,这只是将问题从‘播放’转移到了‘控制’,陷入了一个逻辑循环。


企者不立,跨者不行。
——《老子·第二十四章》

本地音乐服务器

老子诚不欺我,退一步思路就打开了,这思路一旦打开情况也就完全不同了。为什么一定要用 Apple Music 呢?或者说为什么一定要用这种订阅制流媒体服务呢?

毕竟自己喜欢听的、经常听的歌就那么些,对现在的新歌接受度也是越来越低。而平台所谓的推歌功能也推不出什么新东西来。这么说来从云端回归本地播放好像也没什么。

下定决心,开始朝着本地播放的方向寻找解决方案。顺着支持列表往下看,看到了另一个熟悉的名字 —— UPnP。

UPnP 是一整套完整的通信协议标准,自1999年发布以来经历了多次迭代与演进。它广泛应用于各种设备,如路由器、打印机、扫描仪、监控摄像头以及影音播放系统等。要用它来播放音乐需要三个部分来实现。分别是控制器 (Controller)、媒体服务器 (MediaServer)和播放器 (MediaRenderer)。

如果把图中的 “控制器” 换成手机,“媒体服务器” 换成家里闲置的一台存放着音乐文件的电脑,“播放器” 换成一台支持 UPnP 的功放的话,那么这就是一个典型的 UPnP 音乐播放应用场景。 

从示意图可以发现,里面的第1、2、3步执行完过后就没有控制器的事情了。也就是说用来控制播放的手机下达完播放指令后就不参与剩下的工作了。它不但能继续干其它的事情,甚至外出离开这个局域网都不会影响 “播放器” 继续从 “媒体服务器” 读取文件播放。

这个 “播放器” 从 “媒体服务器” 读取文件,就像是一台电脑通过局域网读取另一台电脑上的文件一样。能不能播放,全看播放器自己支不支持这个文件。如果支持,它读取到的就是原始文件,而不是服务器重新编码后的版本。因此它更容易实现 “比特完美“ 的传输。至少在协议层面,不会像 AirPlay 那样,先在发送端把音频统一处理成另一种采样率再进行传输。

然而,UPnP 存在一个显著的问题:它缺乏像 AirPlay 那样由单一厂商主导的严格认证体系。由于标准开放且碎片化严重,不同设备之间的兼容性往往参差不齐,难以保证一致的播放表现。播放器从网络上消失的情况更是频繁出现。这正是为什么在追求高品质流媒体播放时,用户往往更倾向于选择像 AirPlay 或 Google Cast 这样具有高度确定性的标准。

那么我的路线就更清晰一点了:寻找一个既保留了 UPnP 式的灵活性(无过多限制),又具备严格统一标准的协议。

而这一切,都指向了功放支持列表中的另一个名字 —— Roon Ready

Roon Ready,是一种认证标准。它意味着这个设备内置了 Roon 的流媒体技术并经过官方认证。它支持设备通过 Wi-Fi 或以太网连接到 Roon Server(Roon 的服务器端)通信,以获得”比特完美“的高分辨率音频。

它使用一个叫 RAAT (Roon Advanced Audio Transport) 的私有协议。理论上可以在多个房间实现样本级(sample-accurate)的多房间同步播放。被称为 Roon Server 的服务器端拥有诸多 DSP 功能。此外,还拥有庞大的元数据(Metadata)数据库,它会自动检索并补全你音乐库中那些文件缺失的元数据。如专辑封面,流派、作者介绍、艺人背景、评价、歌词等等内容。并且与 TIDAL、Qobuz 这类专业的高解析音乐平台深度联动。

在各大平台上都能找到 Roon 那设计精美的 APP,可以用它来轻松控制播放和探索这些丰富的音乐信息。

这一切让它不再仅仅只是一种 “本地音乐播放解决方案” ,更是一种完整的音乐管理系统。而这一切,几乎都是开箱即用的。

但它有两个缺点。

首先,因为 Roon 提供的功能十分丰富,这就造成了服务器端的资源占用很高,轻轻松松就能达到好几个 G 的内存占用。如果音乐库很大的话占用还可能更高。这让部署 Roon Server 有了很大的局限性。首先如果开启 DSP、拥有大型音乐库,或服务器硬件配置较低,Roon Server 与其他服务共用主机时可能会出现资源竞争。因为一旦内存占用达到 100% 后系统开始频繁调用虚拟内存,整个服务器的响应速度将会变得极其缓慢。此外,受限于硬件配置,对于树莓派 (Raspberry Pi) 这类 ARM 单板机而言,小型音乐库尚可胜任,但面对大型音乐库或复杂 DSP 时便会显得力不从心。

第二,Roon 是一款付费商业软件。它提供按月订阅或按年付款的方案,目前约为 100 元与 85 元人民币/月。它还有一个约为 5640 元人民币的终身订阅选项。

我这个人时常会探索一些新鲜的事物。对于每月需要支付上百元的订阅费来满足 “在家里播放电脑里的歌曲” 这样的需求,我虽然在逻辑上完全理解其价值,但是能理解不代表会接受。


今子有五石之瓠,何不虑以为大樽而浮乎江湖,而忧其瓠落无所容?
—— 《庄子·逍遥游》

Lyrion Music Server + Squeezelite

庄子诚不欺我,如果事先就对某一类事物或人物有了一个预设用途作为前提,然后带着这个预设去判断某一个具体的事物或人物有没有用,这显然是不对的。

要找到一种串流协议,也可以不局限在这台功放支持的协议列表里面找。

时间回到 2001 年,苹果的 iPod 刚刚上市。全世界都在听一种名为 MP3 的数字音频格式。坐在电脑前打开一个名叫 Winamp 的软件,通过放在电脑桌上的电脑音箱听着自己喜欢的歌曲,这仍然是今天很多人的共同记忆。

当时有大量类似 iPod 这种设备被人们粗略的称为 MP3 播放器。把它像 U 盘一样连接上电脑,然后将 MP3 文件复制进去就可以像随身听一样使用。

但问题在于电脑音响的素质普遍偏低,大多都是“能出声”的塑料音箱。而 MP3 播放器也好,或者一个普通的 U 盘也好,在当时它们都太贵了。把 MP3 歌曲刻录成光盘在当时也是很多人的选择,不过这对 CD播放器有要求,刻录光盘这件事同时也是一种有几率失败且繁琐的方式。

即便如此,从 CD、卡带时代过来的人们还是觉得 MP3 方便太多了。但有一个叫 Sean Adams 的人觉得这种方式很笨。

他认为,音乐应该集中存放在一台服务器上,家里的任何播放器都只是"终端",终端可以随时通过网络从服务器点播而无需事先复制到本地。这今天看来很普通的想法,在当时非常超前。

于是他创立了 Slim Devices 的公司。

SliMP3

公司的第一代产品叫 SliMP3。它没有存储空间,自己也不能播放出声音。只有一个 10Mbps 的以太网接口,一对 RCA 输出。它可以从网络读取服务器上的 MP3,然后输出到音响系统。同时还支持网络电台。

SliMP3 连接方式

这就是后来所有网络串流播放器的祖先。

与之配套的是一款音乐服务器软件 SlimServer 。在家里的电脑上安装好后它提供一个网页界面,用户使用任意设备上的网络浏览器可以浏览曲库并控制播放。值得注意的是,从一开始它就支持管理多个播放器。可以在家里的不同房间布置多个 SliMP3 。并且它们可以同步播放或者是分别播放不同的歌曲。

服务器使用名为 SLIMP3 的协议与播放器通信。服务器软件是开源的,协议也是开放的。这在当时吸引了世界各地的开发者。随着发展,Slim Devices 又设计了新的 SlimProto 协议,提供了更丰富的播放器控制能力,也成为后来 Squeezebox 生态一直沿用至今的通信协议。

公司在 2006 年被罗技以 2000 万美元的价格收购,发布了许多名为 Squeezebox 的系列播放器产品。服务器软件先后更名为 SqueezeCenter 、 Squeezebox Server ,最后使用 Logitech Media Server ( LMS )长达13年。

直到智能手机的出现,Spotify 的兴起。消费者越来越倾向于使用手机直接推送音乐。这就回到了文章开头提到的像 AirPlay 这一类技术。

于是罗技认为 Squeezebox 的市场太小了。2012 年左右停止推出新的 Squeezebox 硬件,产品线也逐渐停产。2024 年,在 Logitech 基本停止维护后,社区接管项目并更名为 Lyrion Music Server(LMS 缩写得以保留)。

今天的 Lyrion Music Server 仍然支持 Squeezebox 播放器。因为服务器与播放器之间的通信协议是开放的,所以只要使用同样的协议就可以与 LMS 服务器通信,而不一定得是 Squeezebox 播放器。于是开源社区诞生了多个使用 SlimProto 协议的软件播放器。其中又以2012年由 Adrian Smith 发布的 Squeezelite 最为流行。

Squeezelite 最初是针对 Linux 写的一个没有图形界面的命令行软件。源开发者称其为 “squeezebox 模拟器”。它支持无缝播放,以及最高 384kHz 的采样率。还可以通过 LMS 上的插件实现诸如Qobuz、Spotify、TIDAL等流媒体平台直接串流播放。

目前,用户可以在 Linux、Windows 或 macOS 上直接安装 Squeezelite,也可以使用集成了该引擎的图形化播放器。在安卓或苹果手机上也能找到不止一种内置它的播放器。甚至还有像 piCorePlayer 这样的系统,它集成了 Squeezelite、Lyrion Music Server 和图形界面,是专为树莓派单板机开发的操作系统。通过配置,piCorePlayer 可以作为 连接到 LMS 服务器的一个播放器,也可以自己就是一个 LMS 服务器,还可以即是播放器又是服务器。甚至可以上面都不是,而是一个用来控制其他播放器和服务器的控制器。

近年来,像 Eversolo 这种厂商也已经开始支持连接到 LMS ,或者如 WiiM 这种直接内置了 Squeezelite 。

最终,Lyrion Music Server + Squeezelite 成为了我的解决方案。它的缺点只有一个,但已经被我克服了。这个缺点就是 —— 折腾。

也正是因为可以折腾,它才拥有了今天依然旺盛的生命力。


锲而舍之,朽木不折。锲而不舍,金石可镂。
—— 《荀子·劝学》

服务器端

荀子诚不欺我,他估计也是个喜欢折腾的人。

自家车库里专门用来折腾这类项目的“数据中心”

我解决方案中的 Lyrion Music Server 部署在一台 Intel 十代 NUC 迷你电脑上。

运行中的 Intel 十代 NUC 迷你电脑

我用 FreeCad 建模,并用 3D 打印机打印了固定件将它固定在“洞洞板 (Pegboard)” 上。操作系统为 Ubuntu Server ,一个默认没有图形界面的 Linux 发行版,用以获取更多的可用系统资源。

在 LMS 官方提供的多种部署选项中,我选择了目前社区最主流且维护成本较低的方案:基于 Docker 的容器化部署(Containerized Deployment)。

在部署过程中,利用 Docker 的卷挂载(Volume Mounting)功能,将家中另一台 NAS (网络附属储存 Network-attached storage )上的音乐库文件夹映射到了容器内。这样家里的照片、视频、音频、文档等等文件都集中存放在 NAS 上。除了方便备份和统一管理以外,未来一旦储存空间不够了,买块硬盘装在 NAS 上即可进行扩容,而不用担心这台迷你电脑的储存空间问题。

这里附上我用来部署的 Docker Compose:

services:
  lms:
    container_name: lms
    hostname: lms
    # 使用 host 网络模式以确保端口映射和多播发现(mDNS)正常工作
    network_mode: host
    image: lmscommunity/lyrionmusicserver:stable
    volumes:
      - /home/preston/docker/lms/config:/config:rw    # 配置文件持久化存储
      - nas_music:/music:ro    # 挂载 NAS 音乐库(只读模式)
      - /home/preston/docker/lms/playlist:/playlist:rw # 播放列表持久化
      - /etc/localtime:/etc/localtime:ro    # 同步容器系统时间
      - /etc/timezone:/etc/timezone:ro    # 设置时区
    environment:
      - PUID=1000    # 指定容器内用户 ID(避免权限冲突)
      - PGID=1000    # 指定容器内用户组 ID
      - HTTP_PORT=9000    # Web 界面访问端口
      - TZ=Pacific/Auckland    # 设置服务器时区
    restart: always

volumes:
  nas_music:    # 定义外部存储卷,实现跨设备数据挂载
    driver: local
    driver_opts:
      type: nfs    # 使用 NFS 协议进行网络文件系统挂载
      o: "addr=192.168.1.200,nfsvers=4.1,nolock,soft,ro"    # 配置挂载参数:指定 IP、版本、防止死锁及只读权限
      device: ":/volume1/music"    # 远程服务器上的共享目录路径
LMS 容器的运行状态

LMS 管理着一个拥有超过 18000 首乐曲的音乐库在音乐正在播放的情况下有着极低的 CPU 使用率,以及不到 280 MB 的内存空间占用。


播放器端 · 客厅

我有两个房间有音乐播放的需求。一个是客厅,另一个是主卧室。在客厅我尝试过像 piCorePlayer 和 moode audio player 这种专为树莓派设计的音乐播放操作系统。也试过为 X86 架构设计的 Daphile 系统。而最终我选择了 WiiM Ultra 通过网线连接到交换机作为在客厅的播放器。

播放中的 WiiM Ultra

使用它作为播放器的原因不是因为 piCorePlayer 它们不够好,而是因为 WiiM Ultra 颜值高一点,毕竟是要放在客厅里面的。我是不会承认它是在我摸索解决办法的过程中买来,在后来即便发现了更简单且成本更低的方案后因为 “买都买了” 所以就这么用着了的事实。

关于 WiiM 与 HEGEL 的连接会涉及到一点 玄学 的部分:

我这台 HEGEL H190v 合并式功放自带一个 DAC( 数字模拟转换器 Digital to Analog Converter)。

虽然有可能你之前没有听说过这个东西,但你一定用过它。所有播放数字音频的设备里面都有它。所有 CD 播放器、所有 MP3 播放器、从大哥大时代过后的所有手机、所有蓝牙耳机、所有平板电脑,笔记本电脑等等还有很多,他们内部都有 DAC 。

这台功放自带的这个 DAC 素质很高,至少我自己把家里所有能拿来做对比的设备都拿来反复对比听过了,发现它的声场是最为开阔,音色也是最细腻的。

所以在我的这套系统中,我更倾向于使用它自带的这个 DAC。而必须使用数字输入接口连接才能启用它 。H190v 的数字输入接口有光线、同轴、USB 这三种。其中又以 USB 为最佳的连接方式。通过 USB 连接除了能让功放自带的 DAC 来进行数模转换外,还能启用异步模式而使用功放里面自带的高品质时钟来主导数模转换过程,从而减少抖动(Jitter),同时还能绕过使用光纤或同轴连接需要考虑的线材质量问题。


播放器端 · 主卧室

DENON DRA-500AE 正面

主卧室里面则是一台天龙双声道纯模拟功放 DRA-500AE ,只有 RCA 输入。我利用了同样是收来的一块树莓派 3B 自带的 3.5mm 音频输出口再配合网上找到的热门外壳 3D 打印设计,以及 piCorePlayer 操作系统作为一台 Squeezelite 播放器通过一根
3.5mm 转 RCA 音频线连接到了这台功放上。

Raspberry Pi 3B
装上 3D 打印外壳的 Raspberry Pi 3B 与 DENON DRA-500AE 以及 DENON DCM-500AE

控制端

Lyrion Music Server 提供一个非常好用的网页管理界面。可以在这里实现包括播放控制、服务器配置、插件安装等各种操作。它还可以根据歌曲的信息自动去网上抓去艺人及歌曲信息。

官方插件库里的插件非常强大,除了之前提到的流媒体平台支持外,还可以让服务器承担桥梁的作用,让各种不同协议的播放器实现互通。例如让一个不支持 AirPlay 的音箱支持 AirPlay 从而显示在 iOS 的隔空播放列表里。

网页端的“正在播放”界面
网页端的艺人、专辑、歌曲信息界面

除了 LMS 提供的网页界面外,我使用 LyrPlay 作为 iOS 系统的手机控制 APP 。

LyrPlay 的 “正在播放” 界面

它集成了网页控制端的界面,使用起来与 LMS 网页界面体验统一,丝般顺滑。


第二个问题

很快,这种本地音乐服务器就遇到了第二个问题。

LMS 的随机播放有很多种选择,可以根据歌曲的信息在某些类别里面随机播放。例如在所有 80 年代里面的歌曲里随机播放,在所有爵士乐里面随机播放等等。它非常依赖歌曲信息的准确度,很多时候一张专辑里面的不同歌曲会有多种不同曲风。但歌曲的信息里面往往粗暴的记录整张专辑每首歌都是“流行”这一种曲风。如果要挨着人工去编辑工作量巨大不说,有时候自己也说不清某首歌是个什么曲风。

简单的把随机范围为设置为整个音乐库后,问题就更大了。上一曲还是 Rafael Kubelik 指挥柏林爱乐的德沃夏克第九交响曲的第二乐章,下一首直接就是凤凰传奇的奢香夫人,这种体验很差。


AudioMuse-AI

感谢 AI 大力发展的时代,我们现在有了 AI 来解决问题。而且还是本地的解决方案。

AudioMuse-AI 的 “Music Map”

AudioMuse-AI 是一个开源项目,具体有点复杂。简单点说,它利用专门的本地 AI 分析音乐库里面的每一首歌的声音歌词,相当于挨个去“听”。然后建立数据库把它们记录并归类。最后利用它所支持的 LMS API ,用户可以直接通过它在 LMS 里面生成播放列表。

关于具体可以做到的事,这里直接采用官方的介绍:

聚类归类 (Clustering):自动将音色相似的歌曲归为一组,纯粹基于音乐的实际听感,打造打破流派界限的专属歌单。
即时歌单 (Instant Playlists):只需告诉 AI 你想听什么——比如“高节奏、低能量的音乐”,它就会瞬间为你生成一张歌单。
音乐地图 (Music Map):通过色彩斑斓、基于流派的 2D 地图,以视觉化的方式直观探索你的音乐收藏。
同类歌曲建单 (Playlist from Similar Songs):挑选一首你挚爱的曲目,AudioMuse-AI 就会在你的曲库中揪出所有拥有相似“声音特征”的歌曲,生成一张充满惊喜的探索歌单。
歌曲路径 (Song Paths):在两首歌曲之间搭建一座无缝衔接的听觉桥梁。AudioMuse-AI 会找到最完美的过渡曲目,填补它们之间的听觉鸿沟。
声音指纹 (Sonic Fingerprint):根据你的日常收听习惯生成歌单,精准捕捉并匹配与你近期播放最频繁的曲目风格相似的歌曲。
音乐炼金术 (Song Alchemy):随心调配你的理想氛围!通过对曲目标记“加(ADD)”或“减(SUBTRACT)”来获取精选歌单和 2D 预览图,还能将最终选好的曲目直接导出到你的媒体服务器。
文本搜索 (Text Search):支持通过简单的文字描述来搜索歌曲,词条可涵盖情绪、乐器和流派,例如“平静的钢琴曲”。
歌词深度搜索 (Lyrics Search):打破单纯的声音属性,支持通过主题、故事或寓意(如“关于异地恋的歌”)来深度检索你的曲库。
同类歌曲建单 (Playlist from Similar Songs)

实际体验近乎完美。例如上图所示,AI 在音乐库中选出了 10 首与 The Girl From Ipanema 这首歌最接近的歌曲。通过最下方的绿色按钮即可 利用 API 直接在 LMS 生成播放列表,无需手动添加。

在 LMS 中由 AudioMuse-AI 创建的列表

来自同一个艺人的不同歌曲的风格往往会更接近与彼此,如果不太希望播放列表过多重复出现某一个艺人的音乐,还可以勾选 “Limit songs per artist in results” 选项对单一艺人出现频率进行限制。

勾选 Limit songs per artist in results 后的播放列表

可以看到开启选项后的结果有了很大不同。

歌曲路径 (Song Paths)
通过 “歌曲路径 (Song Paths)” 创建的播放列表

上面的图展示了如何用10首歌从德沃夏克第九交响乐第二乐章平滑过渡到凤凰传奇的奢香夫人。


代价

当你看到“本地”与“AI”这两个词同时出现的时候,就意味着这东西需要大量的算力。大量是什么量?官方主页如此写到:“对于大型收藏(10万首歌曲)或旧硬件,1周以上的分析时间是完全正常的。”也就是说以我目前18000首的音乐库来说,完成对每首歌曲的分析需要的时间是以 “天” 为单位的。

还好,它可以加速。

提供算力的程序是可以并列部署在多个主机上的。

还是以 Docker 容器部署为例:在一台主机上部署被官方称为“Flask“的主容器以及数据库等。它们负责运行网页界面供用户访问,生成、提交分析任务,通过 API 直接在支持的媒体服务器上创建播放列表(例如 LMS),以及管理数据库。

然后在其他多台主机分别部署称为 “Worker“ 的无状态( Stateless )容器。这些 “Worker” 会自己去主机领任务来完成,并传回结果。

因为只在第一次分析以及之后音乐库有变动时才需要这些算力,所以我可以在需要算力的时候尽可能多的用上家里的主机,不用局限于仅使用闲置主机。这既包括那些收来的“破烂儿”,也可以用上日常使用的性能更好的主力机。这几乎是 ”全家出动“ ,我连树莓派都没放过。


部署

首先是“Flask”部署:

services:
  # Redis service for RQ (task queue)
  redis:
    image: redis:7-alpine
    container_name: audiomuse-redis
    ports:
      - "6379:6379" # Expose Redis port to the host
    volumes:
      - redis-data:/data # Persistent storage for Redis data
    restart: unless-stopped

  # PostgreSQL database service
  postgres:
    image: postgres:15-alpine
    container_name: audiomuse-postgres
    environment:
      POSTGRES_USER: audiomuse
      POSTGRES_PASSWORD: audiomusepassword
      POSTGRES_DB: audiomusedb
    ports:
      - "5432:5432" # Expose PostgreSQL port to the host
    volumes:
      - postgres-data:/var/lib/postgresql/data # Persistent storage for PostgreSQL data
    restart: unless-stopped

  # AudioMuse-AI Flask application service
  audiomuse-ai-flask:
    image: ghcr.io/neptunehub/audiomuse-ai:latest # Reflects deployment.yaml
    container_name: audiomuse-ai-flask-app
    ports:
      - "8001:8000" # Map host port 8000 to container port 8000
    environment:
      SERVICE_TYPE: "flask" # Tells the container to run the Flask app
      TZ: "Pacific/Auckland"
      # DATABASE_URL is now constructed by config.py from the following:
      POSTGRES_USER: audiomuse
      POSTGRES_PASSWORD: audiomusepassword
      POSTGRES_DB: audiomusedb
      POSTGRES_HOST: "postgres" # Service name of the postgres container
      POSTGRES_PORT: "5432"
      REDIS_URL: "redis://redis:6379/0" # Connects to the 'redis' service
      TEMP_DIR: "/app/temp_audio"
    volumes:
      - temp-audio-flask:/app/temp_audio # Volume for temporary audio files
    depends_on:
      - redis
      - postgres
    restart: unless-stopped

# Define volumes for persistent data and temporary files
volumes:
  redis-data:
  postgres-data:
  temp-audio-flask: # Volume for Flask app's temporary audio

然后是“Worker”:

services:
  # AudioMuse-AI RQ Worker service
  audiomuse-ai-worker:
    image: ghcr.io/neptunehub/audiomuse-ai:latest # Reflects deployment.yaml
    container_name: audiomuse-ai-worker-instance
    environment:
      SERVICE_TYPE: "worker" # Tells the container to run the RQ worker
      TZ: Pacific/Auckland
      # DATABASE_URL is now constructed by config.py from the following:
      POSTGRES_USER: audiomuse
      POSTGRES_PASSWORD: audiomusepassword
      POSTGRES_DB: audiomusedb
      POSTGRES_HOST: 192.168.1.100
      POSTGRES_PORT: 5432
      REDIS_URL: redis://192.168.1.100:6379/0 # Connects to the 'redis' service
      TEMP_DIR: "/app/temp_audio"
    volumes:
      - temp-audio-worker:/app/temp_audio # Volume for temporary audio files
    restart: unless-stopped

# Define volumes for persistent data and temporary files
volumes:
  temp-audio-worker: # Volume for Worker's temporary audio

"Worker" 只需要注意数据库所在主机的 IP 地址。

用手机访问 AudioMuse-AI 管理页面看到的已连的 “Worker” 接节点列表

可以在上图看到正在工作中的 6 个 “Worker” 节点。


若夫乘天地之正,而御六气之辨,以游无穷者,彼且恶乎待哉!
—— 《庄子 · 逍遥游》

最后

庄子说的 “天地之正” 就是我们所在的这个世界的自然法则。人类在自然法则面前是渺小的。所以他说 “乘” ,主张人应该作为乘客乘坐在这辆自然法则的车上,顺应自然。

虽然这辆车将要开向哪里我们并没有力量去改变它,甚至很多时候都无从知晓。但我们也不是坐在上面就可以了。道家的 “无为” 是需要去 “为” 的,就像老子说的:“为无为,而无不为。”

所以庄子在这里也说 “御”。坐上这趟列车,这段未知的旅途上遇到的风霜雪雨我们可不会坐以待毙,任其肆虐。我们在这个时候就应该成为司机去驾驭它们。

看似我扯的有点远,又有点不务正业。但天下所有的事都是一回事。以同样的态度面对所有的事物,只有这样才能在遇到困难时不慌不慢。甚至能像庄子说的那样 “以游无穷”。

在我看来,从曾经能通宵游戏的 “毁掉的一代”,到如今拿起手柄就感到疲乏;从曾背负重装备登顶 Mount Taranaki 以响应 “山的召唤”,到如今偶有感知腰酸背痛的生理变化——能够平静地看待并接受这些变化,就是 “乘“。而主动去适应新环境、寻找问题的解决方案,就是 “御”。

作为总结,我为整套解决方案制作了一张示意图:

让最后能悠闲享受音乐的过程很折腾,折腾的过程却也同样很享受。当然有第一、第二个问题就会有第三、第四个,例如歌曲信息的编辑与补全,音乐的来源等等。有时间我会继续分享。

在普遍失去耐心的今天,万一真有人一直读到这里的话。由衷的感谢你愿意花费你宝贵的时间在我这里。