ラズベリーパイ、I2S、完全外部クロック、スレーブ入力のめどが立ちました!
現在、I2Sという形式でデジタル音声データをラズベリーパイからDACへ送ることが流行っています。
I2Sは、従来のUSB経由のDACと違って、音がきれいだといわれています。USB-DACでは、DACにわたるまでには、USBの通信モジュール、データをDACの要求通り、生成するモジュールが必要であり、ノイズに汚染される可能性があるからと思われます。
しかしながら、ラズベリーパイのドライバは、ラズベリーパイから送出されるI2Sのクロックは周波数を合わせるために、波長の異なる波形を混ぜているようで、ジッターを意図的に作り出しているようです(「ほーりーさんの日記」の「Volumio でジッターを無理やりなくしてみる」参照)。それは、論理的に私の肌に合いません。
そこで、「三日坊主な私がいつまでブログやれるか...( ̄~ ̄;) 」さんの「Raspberry Pi ALSA I2S ドライバ その2 」その他の記事に注目し、かなり読み込みました。そこでは、ラズベリーパイのカーネルソースを改造し、外部クロックのMCLK(22.5792Mhz、24.576Mhzなど)をDACへ直接入力するとともに、データを出力するためのBCLK(64×FS(サンプリング周波数44.1kなど))をラズベリーパイに入力するとともに、DACへも入力することがされています。ただ、この記事通りにやっても、つまりinsmodのみをやっても、デバイスが登録されず、動きませんでした。
そこで、「new_western_elec」さんの「SabreBerry32のドライバのコンパイル方法(SABRE9018Q2C)」で公開されているデバイスドライバーのソース(今なぜか見れなくなっています)を見て開眼し、カーネルモジュールの.koを直接所定の場所へコピーすることで、デバイスが登録され、動作したので、以下では、その方法について説明していきます。このカテゴリーの過去記事も参考に願います。
1 2014-01-07-wheezy-raspbian 以降のものを「Index of /pub/raspberrypi/raspbian/images」から入手し、SDカードに焼きます。
・最新バージョンだと、いらないファイルもダウンロードされ、大きすぎるだけでなく(1gbを超える)、また、「SabreBerry32のドライバのコンパイル方法(SABRE9018Q2C)」にあるように、dtbsなどのファイルが必要とされ動くかはよくわかりません。
・「三日坊主~」さんのサイト内の「Raspberry Pi 最近のI2S出力事情」を参考にする。
・このとき、以前同じSDカードを使っていた場合には、SDFormatterでパーティションを消さないと動かないかもしれません。
・当該バージョン以降のものであると、すでにi2s用のモジュールの.koファイルが組み込まれているので、容易にI2Sデバイスを登録できますし、また、モジュールをインストールすべき上記.koの所定の場所は、検索することで発見することができます。今回検索したファイルでは、ls -lコマンドで、その2014年作成のものが、insmodをしてモジュールをインストールしても残っていたので、これを上書き保存しました。
・また、volumio1.55でも動くかもしれませんが確認してません。
・ダウンロードに時間がかかる場合には、ctrl+cでコマンドを中止させたうえで、母艦のウィンドウズにダウンロードしたうえで、teraTermの「ファイル」タブを開き→SSH SCP..を開いて、ウインドウズからラズベリーパイへ転送する。
2 各種設定(SSH、ボリューム拡張)などは、このカテゴリの過去記事を参考に願います。
3 「sudo nano /etc/modules」というコマンドを打ち、modulesファイルを編集します。
(1)上記「Raspberry Pi 最近のI2S出力事情」」を参考に、
「snd_soc_bcm2708_i2s
bcm2708_dmaengine
snd-soc-pcm1794a
snd_soc_rpi_dac 」を追加します。
(2)これをコピーして、SSH上で、右クリックでペーストできます。
(3)保存は、ctrl+oで、エンター。ctrl+xで閉じる。
(4)もう一度同じファイルを開いて、修正されたかを確認する。なお、上ボタンを連打すれば、打ち込んだコマンドの履歴が次々に表示されます。
これらのモジュールを追加すれば、上記バージョン以降であれば、自動的にi2sデバイスが認識されます。
その後、rebootで再起動して、「aplay -l」コマンドで、デバイスが登録されているか確認する。
4 カーネルソースファイルをインストールする。
(1)カーネルは要するに、windowsでいうシステムファイルのようなものでOSを構成する核となるファイルです。
(2)カーネルソースは、rpi-sourceコマンドで、インストールできます。
①まず、「sudo apt-get update」をコマンドラインから打ち込みます。
gccというコンパイラがこのコマンドをインストールするのに必要で、gccをインストールするのには、
このupdateが必要です。
②その他、上記「SabreBerry32のドライバのコンパイル方法(SABRE9018Q2C)」を参考にします。
それからrpi-source本家のサイトもご覧ください。通常の場合、コンパイルにつかうgccのバージョンが要求されているものと異なり複数のgcc(4.8、4.9など)をインストールすることになります。上記2014-01-07版のラズビアンをインストールするとgcc4.7もインストールすることになります。それから、コンパイラのgccのうちの、どれを優先的に起動するかの順位付けも行います。
<留意事項>
・このコマンドによれば、symvers?ファイルもインストールされますから、クロスコンパイルも、ラズベリーパイ上での長時間のコンパイルも不要です。
・rpi-updateはここでは不要です。それをするぐらいなら、最初からそれに対応するバージョンをインストールしたほうがいいでしょう。rpi-updateは、かなり時間がかかります(30分オーバー)。
・下手にクロスコンパイルをすると、sudo apt-get updateがものすごい時間がかかった記憶がある。
5 ドライブパスを登録する(のが好ましい)。
カーネルソースの場所のディレクトリは、長いのでディレクトリの文字列を登録します。
①スーパーユーザ(管理者)でログイン。
「sudo su」コマンドの実行。そうしないと、カーネルソースのディレクトリを開くことができない。
②以下のコマンドでカーネルソースを探す。
「cd /root
dir」
③英数文字の長いフォルダを見つける。
「root@raspberrypi:/home/pi# cd /root
root@raspberrypi:~# dir
linux
linux-1981ddebd4d3108cc942680a75ea151a479d65a8
linux-1981ddebd4d3108cc942680a75ea151a479d65a8.tar.gz」
・ここで、「.tar.gz」はカーネルソースの圧縮ファイルでフォルダではない。
③そのフォルダ名称をコピーしてexportコマンドで登録。
「export linux=/root/linux-1981ddebd4d3108cc942680a75ea151a479d65a8」
・ここでlinux-1981・・・以下はコピペする。その内容はバージョンによって、異なる。
・ここにつき、ブログの表示上/linux-の後ろ改行が入っているかもしれませんが、実際は改行は挿入しません。
・「cd $linux」でこのフォルダに入ることができる。
④i2s関連のフォルダのパスを登録する。
・1つの方法は、上記③の登録を前提に
「export bcm=$linux/sound/soc/bcm
export codec=$linux/sound/soc/codecs」
・これでだめなら、他の方法として、
「find / -name “pcm1794a*”」により、pcm1794a.cを探す。
及び、「find / -name “bcm2708-i2s*”」により、bcm2708-i2s.cを探す。
⑤「cd $bcm
cd $codec」が通るかどうか確認する。また、dirによりpcm1794.cやbcm2708-i2s.cを探す。
<注意事項>
このフォルダ名は、おそらく変えないほうが無難?
6 カーネルソースを編集する。
①ここでは、「三日坊主~」さんのサイト内の「Raspberry pi I2S PCM1716でハイレゾ再生 」を引用します。以下の通り、同記事にほぼ従う。
ただし、gpioのところの修正は、とりあえず無視する。RPi B+以降だとその通りの修正が必要になるかも(同サイトの「Raspberry Pi B+ 到着 & I2S出力確認」も参照)。
②「nano $bcm/rpi-dac.c」コマンドで「rpi-dac.c」を編集する。
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
//SND_SOC_DAIFMT_CBS_CFS,
SND_SOC_DAIFMT_CBM_CFS,
・この「SND_SOC_DAIFMT_CBM_CFS」CBクロックマスター、フレームスレーブの意味だそうだが、実際は、ラズベリーパイがBCLKクロック(64fs固定)の入力を受け付けるスレーブのモードになる。<2016/8/6:修正>
ここでのフレームは、LRCKのこと。つまりチャンネルデータのタイミングを規定するLRCKをも、外部入力できる。その点では、現状の方法は、厳密には完全ではないが、分周しているだけなので問題は少ないと考えている。
③「nano $bcm/bcm2708-i2s.c」コマンドで「bcm2708-i2s.c」を編集する。
static struct snd_soc_dai_driver bcm2708_i2s_dai = {
.playback = {
・・・・・
// : disable for now, it causes white noise with xbmc
| SNDRV_PCM_FMTBIT_S24_LE
| SNDRV_PCM_FMTBIT_S32_LE
24ビット、32ビットデータの入力を受け付ける。
24ビットのpcmのファイルは、8ビット×3個ずつ、LR順に読んでいくが、DACへ転送する場合に、32ビット、下8ビット空(パディング)して転送する。おそらくs32が必須(「Raspberry Piを使ってI2S DACのTDA1543の音楽再生」が参考になる。)。
④「nano $codec/pcm1794a.c」コマンドで「pcm1794a.c」を編集する。
static struct snd_soc_dai_driver pcm1794a_dai = {
.name = “pcm1794a-hifi”,
.playback = {
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE
},
};
・これにより、24ビット、32ビットデータを受け付けるようにする。
<2016/8/1:pcm1794a.cの”a”が抜けていました。>
<2016/8/6:追加>
.formatsのところ、変なスペースが入ると認識しない。スペースに見える部分はタブで記述されている。うまくいかない場合には、改行せずにSNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LEという風に続けて書くほうが確実。
それから。通常の24ビットpcmのフォームは、3バイトで表すSNDRV_PCM_FMTBIT_S24_3LEが通常だが、このドライバで認識できるのは、4バイトで表すSNDRV_PCM_FMTBIT_S24_LEのみ。つまり32ビットに足りない部分を0で穴埋めしているデータのみを扱えるので、データ量が1.5倍ぐらいになる。
現状ではファイル変換は、「isi’s 趣味 の電子工作」さんの「Raspberry Piを使ってI2S DACのTDA1543の音楽再生」フォーマット変換(S24_3LE -> S24_LE)ソフトを使うしかない。
SNDRV_PCM_FMTBIT_S24_3LEを追加しても、エラーで止まる。
7 カーネルソースをコンパイルする。
①「cd (コンパイルすべきソースファイルのフォルダ名)
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules」というコマンドが基本になる(rpi-sourceの本家サイト参照)。
②以下のmakeコマンドを実行。
「cd $codec
make -C /lib/modules/$(uname -r)/build M=$(pwd) clean
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules」これにより、
codecフォルダ内のpcm1794.cをコンパイルして、snd-soc-pcm1794.koができる。
・ハイフンに注意。etc/moduleでインストールしたのは、そのインスタンス(参照先を示すファイル)のアンダーバーsnd_soc_pcm1794。
③以下のmakeコマンドを実行
「cd $bcm
make -C /lib/modules/$(uname -r)/build M=$(pwd) clean
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules」
これにより、bcm2708-i2s.cがsnd-soc-bcm2708-i2s.koへ、rpi-dac.cがsnd-soc-rpi-dac.koへそれぞれコンパイルされ、モジュールが生成される。
④以下のinsmodコマンドを実行して、モジュールをインストール
「insmod $bcm/snd-soc-rpi-dac.ko
insmod $bcm/snd-soc-bcm2708-i2s.ko
insmod $codec/snd-soc-pcm1794a.ko 」
<2018.4.24 追加>
40ピンのラズベリーパイB+以降は、
「insmod $bcm/snd-soc-rpi-dac.ko
insmod $bcm/snd-soc-bcm2835-i2s.ko
insmod $codec/snd-soc-pcm1794a.ko 」としてください。
・なお、モジュールを除去するrmmodをするには、依存関係があるので、snd-soc-rpi-dac.ko、snd-soc-pcm1794.ko、snd-soc-bcm2708-i2s.koの順に除去する必要がある。
更新コマンドは以下の通り。後述の⑤だけでも十分かもしれない。
「rmmod $bcm/snd-soc-rpi-dac.ko
rmmod $codec/snd-soc-pcm1794a.ko
rmmod $bcm/snd-soc-bcm2708-i2s.ko
insmod $bcm/snd-soc-bcm2708-i2s.ko
insmod $codecs/snd-soc-pcm1794a.ko
insmod $bcm/snd-soc-rpi-dac.ko」
⑤ 現状組み込まれている.koファイルを探して、直接に④の.koファイルをコピーする。
「cp $bcm/snd-soc-bcm2708-i2s.ko /lib/modules/$(uname -r)/kernel/sound/soc/bcm/snd-soc-bcm2708-i2s.ko
cp $bcm/snd-soc-rpi-dac.ko /lib/modules/$(uname -r)/kernel/sound/soc/bcm/snd-soc-rpi-dac.ko
cp $codec/snd-soc-pcm1794a.ko /lib/modules/$(uname -r)/kernel/sound/soc/codecs/snd-soc-pcm1794a.ko」
<2016/8/1:$bcm/snd-soc-pcm1794-i2s.cではなく$codec/snd-soc-pcm1794a.koでした。>
・上記④だけでは、実際にはデバイスはインストールされないし、更新されない。
・「find / -name “snd-soc-pcm1794*”」
「find / -name “snd-soc-bcm2708-i2s*”」とする。
・ちなみに、snd-soc-bcm2708-i2s.koを直接探してもなぜか出てこない。
・その検索結果は、
/lib/modules/$(uname -r)/kernel/sound/soc/bcm/snd-soc-bcm2708-i2s.ko
/lib/modules/$(uname -r)/kernel/sound/soc/codecs/snd-soc-pcm1794a.ko
・「ls -l ファイル名」でその更新日時を表示する。
・ちなみに、.koと.kを間違えると、大変なことになる。モジュールを組み込めないとかのエラーで、デバイスが起動しない。
8 DACの接続
①この改造デバイスは、DACを選ばない。I2Sに対応してれば足り、I2Cがなくてもよい。
②4つの信号をラズベリーパイと、DACとで行き来させる。
・MCLK(通称マスタークロック、22.5792、24.572Hz)
・BCLK(64×fs(fsは、44.1kなど)固定。
データを搬送するクロック。
このクロックの立ち上がり時のDATAの1、0がDACに取り込まれる。
LR32ビットずつなので、64ビット)
これは、MCLKを74AS161、74F161、74VHC161などにより
MCLKを1/2、1/4.1/8で、分周して生成し
(分周比はラズベリーパイのGPIOで生成するしかない)、
ラズベリーパイのp5の3ピン、およびDACそれぞれに注入する。
・DATA(ビットデータ、ラズベリーのp5の6ピンから、DACへ)
・LRCK(fsと同じ周期。左右のチャンネルデータを仕切る。
BCLKをラズベリーパイに入力するスレーブモードでは、BCLKをもとに生成される)
・DーIN(使わないことに注意。これはクロック入力ではない。
ラズベリーパイへのAD入力(録音など)に使う。)
<2016/8/6追加>
64×fs固定と書いたが、必ずしも固定する必要はなく、2708-i2s.cのドライバのソースの修正でなんとでもなるように思える。bulk_lengthの2倍(LR)がbclk_ratioとされ(482行)、それにsampling_rateを乗算したのがbclkとされている。仕様書を見ると、必ずしもLRCK内に32ビットぴったり収める必要はなく、そうすると後述の分割比によるクロック選択も不要となる。つまり例えば、192k系列だと、LRCKの周波数は、192k×64固定とすればよい。
③74S161の接続は、VCCへ3.3Vを接続する。GNDへグラウンドを接続する。
・ckへMCLK(22.5792等)を接続する。
・MRバー(リセット)へは、リセットがかからないよう3.3Vを接続する。
・Q0、Q1、Q2から、分周したクロックを取り出せるので、
・それをラズベリーパイ、DACのBCKへ注入する。場合によっては、74hc04などのバッファが必要。
④将来的には、22.5792と、24.576を選択する必要があり、
マルチプレクサ、例えば74xxh153などを用いる。
分周比も、マルチプレクサで選択するなどする必要がある。
Q0,Q2、Q3を選択する方法もあるが、xx161のビットを入力する端子に入力することも可能。
9 動作確認
①「aplay -l」(エイプレイ エル)で、デバイスを表示させる。
②「samba」をインストールして、ウィンドウズの母艦から、「ネットワーク」にラズベリーパイを表示させるようにする。
「sudo apt-get install samba」
③nas_allというフォルダを作り、allというフォルダを、ウィンドウズで表示させる。
「sudo mkdir /home/nas_all」
・smb.confファイルを編集する。
「sudo nano /etc/samba/smb.conf」
・ネットアドレスのところ、間違えると、書き込みできない。
# interface names are normally preferred
interfaces = 192.168.11. 127.0.0.0/8 eth0
hosts allow = 192.168.11.
・「11」のところは、各自のルータの設定を使う。
・同ファイルの最後に以下を追加
「[all]
path = home/pi/nas_all
guest ok = Yes
read only = No
force user = pi
」
その他たくさん説明記事がネット上にあるので、それを参考にする。
<2018.3.25 追加>
allというフォルダが見えるものの、パソコンからアクセスできない場合には、nas_allというフォルダの権限を変えます。グループユーザの権限を変える。
chmod g+wrx nas_all
それでもダメな場合には、chmod a+wrx nas_all
とします。これで動きました。
④ファイル再生
上記③で、ウィンドウズには、「ネットワーク」上にラズベリーパイの共有フォルダが見えるはずなので、そこにwav形式の音楽データを入れる。
・「cd /home/pi/nas_all
aplay -D hw:1 xxx.wav」
・ファイル形式は、日本語が混じっていてもよい。
・「-D hw:1」により、i2sのデバイスを選択でき、DACから音声を出力できる。
もっとも、上記①で確認して同じ番号(ここでは「1」)にするのが確実である。
⑤ 44.1kのファイルにもかかわらず、MCLKが49k(本当は48kのはず)、BCKが3Mhz程度となったので、外部クロックが効いている。
・クロック注入をやめると、MCLKの出力も消え、クロックがないからなのか、数秒後にエラーがでる。
⑥ これをみて、確信した。完全外部クロック+スレーブ入力モードになっていると。