Unityでアプリを書くときの、サウンドファイルの暗号化の方法です。
少し前の記事で書いたように、最初ぼくはサウンドファイル、画像をアセットバンドルとして扱い、アセットバンドルを暗号化する方法でやってみたのですが、PCではうまく行きましたが実機(スマホ)ではダメでした(音が鳴らない、画像が表示されない)。どこかに理由はあるのでしょうが、もうこちらで頑張るのはやめて、Unityにアセットとして読み込ませるサウンド、画像自体を最初から暗号化しておき、ゲーム実行時に復号化するようにしました。前々回の記事は画像の話でした。今回はサウンドファイルです。
今回サウンドファイルとして使ったのはwavファイルなので、編集にはwaveモジュール(wavファイルを簡単に扱えるようになるモジュール)があればよいのかも知れません。しかし実はぼくは最初mp3を相手にしていて、そのためにpydubモジュールを使っていました。しかし後で書きますがmp3でなくwavを使うことに決め、その流れでwavファイルをpydubで扱っています。
pydubはFFMpegというのを前提にしているらしく、ぼくは次のサイトを参考にダウンロードしました。
https://jp.videoproc.com/edit-convert/how-to-download-and-install-ffmpeg.htm#1.%20Windows%E3%81%A7FFmpeg%E3%82%92%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89%EF%BC%86%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95%EF%BC%88Windows10%E5%AF%BE%E5%BF%9C%EF%BC%89
(https://jp.videoproc.com/home.htm)
いくつか紹介しているサイトがありますので、ご自身で確認してください。pydubのインストールはすぐでした。こちらも参考になるサイトはたくさんあります。
まずpython側。ここでは単純に、wavファイルのサウンドデータの順序を逆転しました。簡単すぎて暗号とは呼べませんが、実験なので易しい方法で。実戦ではサウンドデータをいくつかのブロックに分けてランダムに並べ替えてしまうとか、さらに本気ならAESなどの強力な暗号を使うなどすればよいと思います。
#---------------------------
from pydub import AudioSegment
import numpy as np
path1 = '…………\\moto.wav'
path2 = '…………\\crypt.wav'
#---------------------------
def reverse0(soundData, soundData1):
N = len(soundData)
for i in range(N):
soundData1[i] = soundData[N - i - 1]
#---------------------------
source = AudioSegment.from_mp3(path1)
soundData = np.array(source.get_array_of_samples())
soundData1 = np.empty(soundData.shape, dtype='int16')
reverse0(soundData, soundData1)
dest = AudioSegment(soundData1.astype("int16").tobytes(),
sample_width = source.sample_width,
frame_rate = source.frame_rate,
channels = source.channels)
dest.export(path2, format = "wav")
#---------------------------
実行するとcrypt.wavができ上がります。サウンドデータを逆順にしたものですから、いわゆる「逆再生」で、そのまま聞くと面白いです。
次にUnity側です。先ほど作ったファイルcrypt.wavをResourcesフォルダに入れます。サウンドデータを読み取った後、reverse0()で逆転し、オーディオクリップsoundにセットします。そしてこれを再生します。もと通りの音楽が流れます。Resourcesフォルダにはdummy.wav(内容は何でもよい)を入れておきます。
--------------------------------------------------------------------------------------
static public void playSound() {
AudioClip ac = (AudioClip)Resources.Load<AudioClip>("dummy");
sound = (AudioClip)Resources.Load<AudioClip>("crypt");
var samples = new float[sound.samples * sound.channels];
sound.GetData(samples, 0);
float buf = new float[samples.Length];
revers0(samples, buf);
ac.SetData(buf, 0);
//メインカメラにAudioSourceがアタッチされている前提
GameObject go1 = GameObject.Find("Main Camera");
audio = go1.GetComponent<AudioSource>();
audio.PlayOneShot(ac);//鳴らす
}
//----------------------
static void revers0(float buf1, float[] buf2) {
int N = buf1.Length;
for (int i = 0; i < N; i++) {
buf2[i] = buf1[N - i - 1];
}
}
#---------------------------
謎のdummy.wavを使っています。最初はオーディオクリップsoundからGetData()し、サウンドデータを編集してsoundへ書き戻してsoundを再生、とやっていたんですが、こうすると変更後のサウンドデータがcrypt.wavに反映されてしまうらしく、2度目以降におなじ関数を実行したときにうまくありません。そこで、このようなコードにしました。もっとスマートな方法があるかも知れませんが。
上で書いたように、最初ぼくは(wavファイルより)サイズの小さいmp3を相手にすることを考えていました。しかし0xFFFFとXOR(排他的論理和)して暗号化するとか、他、いくつかやってみたのですがうまく行きませんでした。Unityで再生してももと通りにならないのです。どうもおかしいと思い、サウンドデータの個数をPythonとUnityとで比較したら、同じmp3なのに違うではありませんか!? もともとmp3はサウンドデータは32bit float だそう。これをpydubは16bit整数として読み取ります。Unityでは-1から1までのfloatとして扱うようで、その過程で何か起こるのか、それとも他の原因なのか分かりません。しかしwavを使うとサウンドデータのサイズはピタリと一致します(これが当たり前なんですが……)。もういいや、と結局サイズが大きくなるのは覚悟の上でwavファイルにしたわけです。それならwaveモジュールでも扱えると思うんですが、せっかくここまでpydubでやってきているのでそのままpydubで。
これで一応準備完了、ゲーム作成にかかります。
なお、waveモジュールの簡単な使い方は拙著『Python基本サウンドプログラミング~周波数分析,高速フーリエ変換(FFT)入門』にも解説があります。
せっかくだからwaveモジュールを使ってやり直すかな……と考えていますが、もう面倒なのでこのままいくかも。
この本、地味~に売れてうれしいです。購入いただいた皆様に感謝いたします!
追記:
waveモジュールでやってもうまくいきました。次の記事にpythonのコードを載せます。これはpythonに標準で含まれているそうです。wavファイルを使うことに決めておけば、pydubのインストールもFFMpegも使う必要はありません。
なお、mp3をwavファイルに変換することもできます。pydubを使えばすぐです。