Cassette Drive:磁带机时光机
几个月前,我决定升级我的家庭网络:为了进一步试验网络技术,我把我那台疲惫不堪的 Telmex 调制解调器/路由器的路由功能,换成了运行 OpenWRT 的 ER605v2。
计划很简单,现实却不然。时断时续的 WAN 掉线、奇怪的射频噪声,还有一个盯着网口指示灯的漫长夜晚。
在恢复出厂设置和翻论坛帖子的间隙,我点开了一段老式拨号上网声音的 YouTube 视频,结果一头扎进了兔子洞。调制、解调、纠错、同步音,整套模拟到数字的握手故事。几个小时后,我开始勾勒一个新点子:如果我能用软件造一个小小的磁带机,把文件以音频的形式存到磁带上会怎样?
于是就有了现在。Cassette Drive 是一个小巧的 Python 工具,可以把字节变成音频,再变回来。它使用 MFSK(多频移键控)把比特映射到音调上,添加前导码 + 同步字来定位有效载荷,并用 Reed-Solomon 纠错码包裹数据。结果是一条现代而有韧性的数据流,足以经受廉价磁带机带来的种种混乱。我在 Amazon 上买了一台 20 美元的磁带录放机,还有一盒五盘装、每面 45 分钟的磁带,突然之间,我就有了一种全新的存储介质!你知道人们常说的 3-2-1 备份原则吧……它肯定改成 3-3-1 原则会好得多,对吧?对吧???
概述
编码器接收一个文件,构建一个包含音频设置和元数据的紧凑头部,把数据分割成固定大小的块,并添加 Reed-Solomon ECC。然后它用前导码(交替的 0/1 比特)和同步字对所有内容进行封装,使解码器能够锁定信号。调制解调层使用 MFSK:每个符号是一个音调,每个音调编码多少比特取决于你选择了多少种不同的频率。在回程中,解码器对录音进行带通滤波,找到同步模式,用 Goertzel 检测器解调符号,重建原始文件,并用 CRC-32 进行校验。
我的第一个实验:磁带上的 Nala
我想存储的第一样东西,是我的猫 Nala 的一张照片。我用 ImageMagick 把图片缩小到 5 KB 以内,并将其编码成 WAV 文件。然后我通过磁带录音机播放这段音频,倒带,再把它录回我的笔记本电脑进行解码。
我本来预期会有错误,结果第一次就得到了完美的 CRC!公平地说,仅仅存储这张 5 KB 的图片就用掉了整整 45 分钟的磁带(作为参照,如果我把它存成 base64 字符串,用 8pt 字号轻松就能印在一张信纸上),但它成功了!
底层是怎么运作的?
Cassette Drive 有三层:
- 封装与元数据
- 一个紧凑的二进制头部存储采样率、符号率、音调间隔、文件大小和文件名。
- 头部包含一个 CRC-32,让解码器可以确认整个文件完好无损。
- 纠错
- 每个数据块都由 Reed-Solomon ECC 保护,与二维码和 CD 使用的原理相同。
- 你可以调整 ECC 百分比,以速度为代价换取更强的抗干扰能力。
- 调制解调层
- MFSK 把比特组变成音调(音调越多 -> 每个符号的比特越多)。
- 交替比特组成的前导码为解码器提供稳健的对齐目标。
- 同步字标记有效载荷的边界,使数据流可以按字节对齐。
这一切都被设计成能容忍真实世界的噪声:电机抖动、磁带嘶嘶声,或是桌面上乱糟糟的走线。
针对真实录放设备进行校准
校准模式会生成一个包含多种符号率的 WAV 文件。
你把它通过你的音频链路播放,然后分析录音,找出仍能清晰解码的最快速率。这正是磁带机展现实力的地方。有些能很好地处理较高的符号率,有些则需要更慢、更宽容的设置。至于我那台廉价的录音机,它能承受的并不多。
如何试试
如果你想复现这个实验,从小处着手。用宽容的设置(较低的符号率和较高的 ECC)编码一个小于 5 KB 的文件。成功之后,你可以尝试提高速度。
试试解码这条消息吧!
# 克隆仓库
git clone https://code.marvil.co/dumb-stuff/cassette-drive.git
cd cassette-drive
# 创建虚拟环境并安装依赖
python -m venv .venv
pip install -r requirements.txt
# 解码示例消息(请先下载 hello.wav 文件并放入 modulated/ 文件夹!)
python -m src decode --input modulated/hello.wav --output demodulated/hello.txt或者,编码你自己的文件:
# 编码
python -m src encode --input in/myfile.bin --output modulated/myfile.wav
# 录制之后解码(需匹配相同的音调/频率设置)
python -m src decode --input modulated/myfile_recorded.wav --output demodulated/myfile_decoded.bin最后的思考
从 Telmex 升级到 OpenWRT 的过程一团糟,但它促使我做了这个小而酷的项目。也许把它用于存储是个馊主意,但用于学习却是个绝妙的主意 :)
如果你试了,我想看看你的磁带。请在留言板告诉我!