摩斯密码深度指南:从电报到 Web Audio API 音频合成

摩斯密码的诞生与本质

摩斯密码(Morse Code)由萨缪尔·摩斯与阿尔弗雷德·维尔于 1830 年代发明,最初用于电报传输。它本质上是一种用长短信号的组合表示字符的二进制编码系统:

  • .(dit / dot):短信号
  • -(dah / dash):长信号,时长为短信号的 3 倍

每个字符由若干点划组合而成,例如 S = ...O = ---,因此 SOS 的摩斯码为 ... --- ...,这也是最广为人知的国际求救信号。

配套工具:摩斯密码编解码工具

国际摩斯密码编码表

国际摩斯密码(ITU-R M.1677)覆盖拉丁字母、数字与常用标点:

字母

字符字符字符
A.-J.---S...
B-...K-.-T-
C-.-.L.-..U..-
D-..M--V...-
E.N-.W.--
F..-.O---X-..-
G--.P.--.Y-.--
H....Q--.-Z--..
I..R.-.

数字

数字数字
1.----6-....
2..---7--...
3...--8---..
4....-9----.
5.....0-----

常用标点

标点标点
..-.-.-?..--..
,--..--'.----.
!-.-.--/-..-.
(-.--.)-.--.-
&.-...:---...
;-.-.-.=-...-
+.-.-.--....-
_..--.--".-..-.
@.--.-.

PARIS 标准与 WPM 速度

什么是 PARIS 标准?

摩斯密码的速度单位是 WPM(Words Per Minute,每分钟字数)。但”一个字”到底有多长?业界采用 PARIS 作为标准字:

P  = .--.  (4 个信号 + 3 个字符内间隔 = 8 单位)
A  = .-    (2 个信号 + 1 个间隔 = 4 单位,但末尾再加间隔)
R  = .-.   (3 个信号 + 2 个间隔 = 7 单位)
I  = ..    (2 个信号 + 1 个间隔 = 4 单位)
S  = ...   (3 个信号 + 2 个间隔 = 7 单位)
单词间隔 = 7 单位

经过精确计算,PARIS 一个字的总时长 = 50 个 dot 单位。因此:

WPM = 60 秒 / 一个 PARIS 字的时长
一个 PARIS 字的时长 = 50 × dot 时长
→ dot 时长 = 60 / (WPM × 50) = 1.2 / WPM 秒 = 1200 / WPM 毫秒

时序规则

基于 dot 时长,所有时序都有精确比例:

元素时长(单位:dot)20 WPM 时的毫秒数
dot (.)160 ms
dash (-)3180 ms
字符内间隔(点划之间)160 ms
字符间隔(字母之间)3180 ms
单词间隔(词之间)7420 ms

WPM 速度参考

WPMdot 时长适用场景
5240 ms初学者练习
1392 ms业余无线电入门级
2060 ms业余无线电标准速度
3040 ms熟练操作员
40+30 ms高速 Contest 比赛

分隔符约定

摩斯密码的纯点划字符串无法直接区分字符与单词,必须依赖分隔符

字符内分隔

同一字符的点划之间无任何符号,紧密相连:

  • A = .-(dot 紧接 dash,无分隔)

字符间分隔

不同字符之间用空格分隔:

  • SOS = ... --- ...(字符之间各一个空格)

单词间分隔

不同单词之间用 / 或多个空格分隔:

  • HELLO WORLD = .... . .-.. .-.. --- / .-- --- .-. .-.. -..

容错处理

实际使用中分隔符并不统一,优秀的解码器应当容忍:

  • ...---...(无分隔)→ 应识别为 SOS
  • ... --- ...(多空格)→ 等价于单空格
  • ... / --- / .../ 两侧带空格)→ 等价于单词分隔
  • ...|---|...(用 | 代替 /)→ 等价于单词分隔

配套工具的解码逻辑先做归一化:将 | 替换为 /,再统一 / 格式,最后按 / 切分单词、按空格切分字符。

Web Audio API 音频合成原理

摩斯密码的音频播放是工具实现的核心难点。我们采用 Web Audio APIOscillatorNode + GainNode 组合生成纯正弦波 beep。

为什么不用 <audio> 标签?

  • 体积成本:预录音频文件会增加数十 KB 体积
  • 灵活度:WPM、音高、波形调整需要实时计算
  • 精度控制:精确到毫秒的时序调度

AudioContext 调度模型

Web Audio API 采用图模型:音频节点连接成链,最终输出到 ctx.destination(扬声器)。所有时间都基于 ctx.currentTime,这是绝对时间轴,不受主线程阻塞影响。

// 创建音频上下文(浏览器策略要求用户交互后才能恢复)
const ctx = new AudioContext();
if (ctx.state === 'suspended') {
  await ctx.resume();
}

// 调度一个 beep:在 startTime 开始,持续 duration 秒
function scheduleBeep(ctx, startTime, duration, frequency) {
  const oscillator = ctx.createOscillator();
  const gain = ctx.createGain();

  oscillator.type = 'sine';           // 正弦波,音色纯净
  oscillator.frequency.value = frequency;  // 频率(Hz),常见 600 Hz

  // ADSR 包络简化版:快速淡入 + 持续 + 快速淡出
  // 关键作用:避免波形突变产生的"爆音"(click artifact)
  const fadeTime = Math.min(0.005, duration / 4);
  gain.gain.setValueAtTime(0, startTime);
  gain.gain.linearRampToValueAtTime(0.3, startTime + fadeTime);
  gain.gain.setValueAtTime(0.3, startTime + duration - fadeTime);
  gain.gain.linearRampToValueAtTime(0, startTime + duration);

  oscillator.connect(gain);
  gain.connect(ctx.destination);

  oscillator.start(startTime);
  oscillator.stop(startTime + duration);
}

为什么需要淡入淡出?

正弦波在 t=0 时若直接拉到 0.3 振幅,会产生阶跃信号,频谱上会引入宽带噪声,听感上为”啪”的爆音。linearRampToValueAtTime 在 5 ms 内线性爬升振幅,让波形平滑过渡,听感上为纯净的”嘀”声。

时序累加

摩斯密码的每个元素必须严格按时长累加:

const dot = 1200 / wpm;  // 基本单位(毫秒)
let timeOffset = ctx.currentTime + 0.05;  // 留 50 ms 缓冲

for (const ch of morseString) {
  if (ch === '.') {
    scheduleBeep(ctx, timeOffset, dot / 1000, freq);
    timeOffset += dot / 1000 + dot / 1000;       // 信号 + 1 dot 间隔
  } else if (ch === '-') {
    scheduleBeep(ctx, timeOffset, (3 * dot) / 1000, freq);
    timeOffset += (3 * dot) / 1000 + dot / 1000;  // 信号 + 1 dot 间隔
  } else if (ch === ' ') {
    timeOffset += (2 * dot) / 1000;              // 已含 1 dot 间隔,补 2 dot 凑 3 dot 字符间隔
  } else if (ch === '/') {
    timeOffset += (4 * dot) / 1000;              // 已含 3 dot 字符间隔,补 4 dot 凑 7 dot 单词间隔
  }
}

TypeScript 严格模式下的陷阱

在 strict 模式下,useRef<AudioContext | null>(null) 创建的 ref 类型为 MutableRefObject<AudioContext | null>。常见错误:

// ❌ 错误写法:let 变量在 await 后丢失 null 收窄
let ctx = audioCtxRef.current;
if (!ctx) {
  ctx = new AudioContext();  // 此处赋值
}
// 此处 ctx 仍是 AudioContext | null(await 后 let 收窄失效)
await ctx.resume();  // TS 报错:possibly null

// ✅ 正确写法:用 const 在赋值后保持收窄
if (!audioCtxRef.current) {
  audioCtxRef.current = new AudioContext();
}
const ctx = audioCtxRef.current;  // const 在条件分支后保持收窄
if (!ctx) return;
await ctx.resume();  // TS 通过

此外,旧版 Safari 使用 webkitAudioContext 而非 AudioContext,需做兼容:

const Ctx = typeof AudioContext !== 'undefined'
  ? AudioContext
  : (window as any).webkitAudioContext;
if (!Ctx) return;
audioCtxRef.current = new Ctx();

中文摩斯密码的局限性

摩斯密码原生不支持中文字符,因为它是为 26 个拉丁字母设计的。中文摩斯码的几种历史方案都有明显缺陷:

方案一:电码本(四位数字)

早期中文电报采用四位数字编码,每个汉字映射为 4 位数字,再转为摩斯码:

中 = 0022 → ----- ----- ..--- ..---
文 = 2429 → ..--- ....- ..--- ------

缺点

  • 需要查阅电码本,无法直观记忆
  • 一个汉字要发 16 个点划,效率极低
  • 电码本有 9000+ 字,无法扩展

方案二:拼音转写

将中文转为拼音,再用拉丁字母摩斯码:

中文 = ZHONG WEN → --.. .... --- -. --. .-- . -.

缺点

  • 同音字无法区分(“中”和”钟”都是 ZHONG)
  • 拼音本身有歧义(ZH/CH/SH 的双字母表示)
  • 失去汉字的语义信息

方案三:Unicode 码点

理论上可以将汉字的 Unicode 码点转为数字再编码,但实际不可行:

中 = U+4E2D → 4E2D → ....- . ..--- -..

缺点

  • 一个汉字 6-8 个点划,无意义序列难以人工解码
  • 完全失去可读性

工具实现策略

配套工具采用保守策略:仅支持 ASCII 字符集(A-Z、0-9、常用标点)。输入中文时给出明确警告:

以下字符无摩斯码对应,已跳过:你 好

这避免了”伪摩斯码”的误导,同时通过警告提示用户输入范围。

实际应用场景

1. 紧急求救(SOS)

SOS 是国际通用的求救信号(... --- ...),其特点是三短三长三短的节奏感强,便于识别。在无通信设备的场景下,可用光源(手电筒)、声音(敲击)、反光镜发送。

工具可用于:

  • 教学演示 SOS 节奏
  • 验证练习是否正确
  • 调整不同 WPM 下的听感

2. 业余无线电(Ham Radio)

业余无线电爱好者至今仍在使用摩斯密码(CW 模式),原因:

  • 极低信噪比下仍可通信:当语音无法辨识时,CW 信号仍能解码
  • 频谱效率高:极窄带宽(约 100 Hz)即可传输
  • 跨国通用:无语言障碍,纯符号通信
  • 法规要求:某些频段或等级的执照要求掌握 CW

工具可作为 CW 学习辅助,调整 WPM 从 5(初学)到 20(标准)再到 30+(高级)循序渐进。

3. 教学与娱乐

  • STEM 教育:编程教学中用作”编码解码”的入门案例,概念简单但涉及映射、分隔符、时序
  • 密室逃脱 / 解谜游戏:摩斯密码是经典的解谜元素
  • 影视致敬:理解电影中摩斯密码情节(如《星际穿越》的”Stay”)

4. 无障碍辅助

视障人士有时使用摩斯密码作为输入方式(通过敲击触发字符)。Web 工具可用于学习与练习。

5. 嵌入式与 IoT

嵌入式设备常用 LED 或蜂鸣器输出状态码,摩斯密码是紧凑的编码方案:

设备故障 = ERROR → . .-. .-. --- .-.

工程师可用工具快速生成对应摩斯码与音频,验证设备输出。

性能与体积考量

算法复杂度

  • 编码:O(n),遍历字符串查表
  • 解码:O(n),遍历摩斯码字符串查反向表
  • 音频调度:O(n),n 为摩斯码字符数,每个字符一次 scheduleBeep 调用

内存占用

  • 编码表:约 60 个键值对,< 2 KB
  • 反向表:Object.fromEntries 一次构建,< 2 KB
  • AudioContext:单例复用,约几 MB(浏览器实现)

Bundle 体积

整套工具(含 React 组件 + 编解码逻辑 + Web Audio 调度)压缩后约 5-7 KB,远低于 200 KB 的红线。

与其他编码工具的对比

工具字符集用途是否双向
Base64任意二进制数据传输
Hex任意二进制调试、低层查看
摩斯密码ASCII 字母数字标点音频、电报、教学
URL 编码URL 中的特殊字符URL 参数传递
HTML 实体HTML 中的特殊字符防 XSS、显示特殊字符

摩斯密码的独特价值在于音频输出时序调度,是其他编码工具所不具备的。它更偏向”通信”而非”数据表示”。

总结

摩斯密码看似过时,实则在应急通信、业余无线电、教学、IoT 等场景仍有实用价值。本文配套的摩斯密码编解码工具实现了:

  • 双向编解码:文本 ↔ 摩斯码,容错处理多种分隔符
  • 音频播放:基于 Web Audio API,精确遵循 PARIS 标准时序
  • 可调速度:5-40 WPM,覆盖初学到熟练
  • 可调音高:300-1000 Hz,适配不同听感偏好
  • 速查表:内置完整字母数字标点对照
  • 中文提示:输入非 ASCII 字符时给出明确警告

全部计算在浏览器本地完成,零网络请求、零依赖、零追踪,适用于学习、教学、应急通信练习等场景。