Enludus
お問い合わせ

ファイルを音で送ったら、秒速 1.2 ビットだった

Logiscore で、スピーカーから鳴らした音を 1m 先の iPhone で録音し、元のファイルを 1 バイトも違わずに戻せるようになるまでの記録です。

Logiscore は、テキストやソースコードを音にして、スピーカーで鳴らし、別の端末で録音した音から元のデータをそのまま戻すブラウザツールです。変換も復元も、Rust を WebAssembly にしたエンジンがブラウザの中で行い、ファイルはどこにもアップロードしません。

この記事では、「空気を通して本当に戻るのか」を確かめたときの話を書きます。

2 つのモード

Logiscore には大きく 2 種類の出力があります。

  • MIDI(Auto / Fast / Music):1 音に 1 バイトを載せる。上位 4 ビットを 16 音の音階の音の高さに、下位 4 ビットを音の強さにする。データは先に zlib(レベル 6)で圧縮する。
  • WAV(Reliable / Secure):実際にスピーカーで鳴らして、マイクで録音して戻すためのモード。8kHz モノラルの音声に、誤り訂正をかけたデータを載せる。

MIDI は音楽として聴けるのが楽しい一方、DAW に読み込んで書き出すと復元できなくなることがあります。空気を通して送るなら WAV の方です。

Logiscore の画面。左に Rust のソースコード、中央でモード「おまかせ・MIDI」を選び、右に変換した MIDI の先頭の音が五線の上に音符で表示されている
MIDI モード。6 行のコードが 1,420 バイトの MIDI になる

音で送るしくみ

WAV モードでは、16 種類の音の高さで 4 ビットずつ送ります。いちばん保守的な設定では、1 音 80 ミリ秒、そのあと 40 ミリ秒の休み。ほかに、録音の始まりを見つけるための 2 音の合図と、データの長さを知らせる部分があります。

音の高さを聞き分けるのには FFT ではなく Goertzel 法を使っています。決まった周波数だけを調べればよいので、これで足りますし、依存ライブラリを増やさずに済みます。

誤り訂正:Reed-Solomon を使わなかった理由

空気を通すと、どうしてもビットが化けます。そこで次の順に重ねています。

  1. CRC-32 で、壊れていないかを最後に確かめられるようにする
  2. Hamming 符号(13,8)で、1 ビットの誤りを直す
  3. 8 つの符号語をまたいでビットを並べ替え、まとまった雑音を分散させる
  4. 同じものを 3 回送り、多数決をとる

定番の Reed-Solomon は使っていません。外部のライブラリを入れずに済み、WebAssembly のサイズも小さく、中身を自分で確かめやすいからです。その代わり、送る量は元の約 4.875 倍になります。速さより、確実に戻ることを優先しました。

実験:1m 先の iPhone で録音する

MacBook のスピーカーから鳴らし、1m 離れた iPhone のボイスメモで録音して(M4A)、その録音を読み込みます。

最初は戻りませんでした。調べると、届いたデータの誤りはたった 3 ビット。誤り訂正なら直せる量です。原因は、誤り訂正の手前で、音の小さい記号を「弱すぎる」と判断してパケットごと捨てていたことでした。この足切りを見直して、誤り訂正まで届くようにしたところ、元のファイルが 1 バイトも違わずに戻りました。M4A の変換で生まれる 2 サンプルのずれも吸収できています。

秒速 1.2 ビット

正直な数字も書いておきます。いちばん保守的な設定で約 137 秒、実際の録音で約 140 秒かかり、速さにすると約 1.2 ビット毎秒です。音もかなりシンセっぽく、少し不気味です。

モード「空気伝送・WAV」で変換した画面。環境の選択、速さと確実さのスライダー、予想再生時間 3 分 47 秒が並び、右の出力欄に「元のデータに戻せることを確認しました」と出ている
空気伝送モード(標準の設定)。見本の 6 行のコードで、予想再生時間は 3 分 47 秒

速くはありません。でも、ネットワークを使わず、サーバーも通さずに、音だけでデータが届く。そこに面白さがあると思っています。

パスワードをかけて送る

Secure モードでは、パスワードから鍵を作り、暗号化してから音にします。

  • 鍵の生成:Argon2id(メモリ 64MiB、3 回、1 レーン)
  • 暗号化:ChaCha20-Poly1305

PBKDF2 は総当たりに強くするためのメモリを使わないので選びませんでした。逆に、64MiB を確保できない端末では Secure モードが動かない可能性があります。

途中から録音しても戻せるように

いま取り組んでいるのは、ループ再生の途中から録音を始めても戻せるしくみです。データを 512 バイトずつに分けて、それぞれに CRC を付け、どの順番で届いてもよいようにしました。3 回のループがどれも単独では壊れていても、音ごとの確からしさで重みを付けて合わせると戻せる場合があります(合わせたあとも CRC は必ず確かめます)。

まだ画面には出しておらず、iPhone での実録音の確認も残っています。3m・5m の距離や、ほかの端末での確認もこれからです。続きが書けたら、また記事にします。


変調方式、Goertzel による検出、誤り訂正のビット配置、暗号のエンベロープ、チェックポイントの分割まで、実装をソースごと追った技術編は「Logiscore の音響伝送を全部書く」に書きました。