Raspberry Pi + Audio ドキュメント

まえがき

コンピュータが音楽を再生する仕組みとは、どういったものでしょうか。

iTunesの再生ボタンやaplayコマンドを使えば音楽を再生できるといったことは、 誰でも知っていることです。

しかしその裏側の処理――アナログ世界の音波をディジタル・システムが処理して、 さらにそのシステムをカーネルが制御するといった流れについては、 殆どの人にとって、未知の世界と言えるでしょう。

かくいう私もそうした分野についてはこれっぽっちも知識がありません。

だからこそ、手近なコンピュータであるRaspberry Pi(ラズパイ)を通じて、 コンピュータが音を扱う仕組みをす少しでも知りたいと思い、 このような研究を始めました。

本ドキュメントは二部構成になっております。

一部では雑誌に掲載されたオーディオ製作ガイドを自分で追ってみます。

二部では一部で作成したオーディオに対して独自に工夫を加えていきます。

このドキュメントが読者の方のお役に立てば幸いです。

目次

  1. ラズパイ・オーディオの製作
    1. イントロダクション
    2. 製作物仕様
    3. 製作レポート
      1. 事前準備
      2. イメージディスクの作成
        1. Yocto開発環境の準備
        2. bitbakeの実行
      3. 動作確認
  2. 多機能オリジナルオーディオ
    1. イントロダクション
    2. 製作レポート
    3. 参考文献

ラズパイ・オーディオの製作

イントロダクション

Interface 2015年12月号(CQ出版) を参考にラズパイ・オーディオを作成します。

雑誌の手順を追うだけといえば簡単そうですが、 トラブルに遭遇するというのも世の常です。

ここでは手順の概略と、 雑誌には載っていないトラブルへの対処をまとめていきます。

雑誌の丸写しなどをするつもりは毛頭ないので、 文書構成上必要のない情報はばっさりと切り捨てていく方針です。

詳細な解説が必要な場合にはInterface誌をご覧ください。

製作物仕様

この部で作成するオーディオは以下の機能を持っています。

  • 音声再生機能
  • 音声録音機能
  • 再生・録音対象のファイルを選択する機能

以上の機能は外部のボタンからの操作によって行います。 ボタンとオーディオは Raspberry PiのGPIOを通したシリアル通信によって接続されます。

製作レポート

この章では実際に雑誌の手順を追ってみたときの体験記と、 その梗概を示します。

雑誌通りに進めても望みのオーディオは完成しません。 (少なくとも、私の場合。)

私が嵌った落とし穴を重点的にまとめていきます。

事前準備

まずは必要な機材を揃えなくてはなりません。

以下に私が購入したもののリストを示します。

  • Raspberry Pi 2
  • microSDカード
  • DACコーデック
  • 無線子機
  • ブレッドボード
  • 抵抗
  • LED
  • スイッチ
  • メス―メスジャンパ

実はこのリスト、雑誌に書いてある機材とは少し内訳が異なります。

その上、このリスト上の機材(特に電子工作部品)の中には使わないで済むものもあります。

DACを載せてラズパイに繋げられればそれで立派なオーディオが完成します。

とは言ってもあくまでInterface誌に手順を追ってみるというのが この部のコンセプトなのですから、一応抵抗なども購入しました。

イメージディスクの作成

ここではデバイスドライバ開発~自作OS作成までの手順について触れます。

しかし残念ながら、最初はそんな創作的なことには挑戦しません。

OS作成に使用するツール、Yoctoと言うのですが、 こいつでビルドを完了するまでの手順が実に問題だらけで大変なのです。

そこで、まずは無事に型通りのOSを作れるようになることを目指します。

Yocto開発環境の準備

公式ドキュメントを参考に進めればよいでしょう。

予めLinuxマシンを用意しておきます。 私は以下の環境を用いることにしました。

  • マシン : VirtualBox
  • OS  : Ubuntu 14.04
  • メモリ : 2048MB
  • 容量  : 500GB(固定)

随分贅沢なスペックに思えますが、 メモリ1024MBでハングアップを確認したため、倍の2048MBにしました。

ストレージ500GBは過剰かもしれません。 それでも最低50GBは欲しいです。 (後述する理由により可変サイズにはできなかったので、最初から多めに取りました。)

そして何より無制限のLAN環境が必要です。

Yoctoを走らせると毎秒30MB程の途方も無い接続をすることが多く、 WiMAXやキャリアの回線だとあっという間にパンクします。

さて、マシンの用意ができたのであれば、セットアップに移りましょう。

まずはbitbakeに必要なパッケージをインストールします。

$ sudo apt-get install gawk wget git-core diffstat unzip texinfo gcc-multilib build-essential chrpath socat libsdl1.2-dev xterm

次にYoctoをgitからクローンしてきます。 公式ドキュメントでは最新版を使うよう書いてありますが、 コンパイラなどのバージョンの問題があるため、 masterブランチではなくfidoブランチを使用することにします。

ちなみにInterface誌とはブランチ指定のコマンドが少し違っています。 私の場合は同誌のコマンドが通らなかったのでこちらを使用しましたが、 通るならばどちらでも良いかと思います。

$ mkdir -p ~/work/rpi2
$ cd ~/work/rpi2
$ git clone -b fido git://git.yoctoproject.org/poky.git

さらにbitbakeに必要なレイヤをダウンロードします。

なおInterface誌ではさらにmeta-cqrecorder-rpiをダウンロードするよう 書いてありますが、2015年12月末日現在、meta-cqrecorder-rpiのリポジトリは空です。

自力で書いてみろということでしょうか? それともただの編集ミスでしょうか。それはわかりませんが、 meta-cqrecorder-rpiについてはここでは放っておきます。

$ cd ~/work/rpi2/poky
$ git clone git://git.yoctoproject.org/meta-raspberrypi
$ git clone https://github.com/mickey-happygolucky/meta-ssm2603-rpi.git

そして環境設定スクリプトを実行します。

$ cd ~/work/rpi2
$ source poky/oe-init-build-env build_ssm

最後にbuild_ssm内の.confファイルを編集します。 bblayer.confとlocal.confというふたつのファイルが対象です。 まずはbblayer.confから。

$ sudo vim ~/work/rpi2/build_ssm/conf/bblayer.conf

bblayer.confは以下のように書き換えてください。 (/home/ubuntu...のubuntuはユーザ名です。)

# LAYER_CONF_VERSION is increased each time build/conf/bblayers.conf
# changes incompatibly
LCONF_VERSION = "6"

BBPATH = "${TOPDIR}"
BBFILES ?= ""

BBLAYERS ?= " \
  /home/ubuntu/work/rpi2/poky/meta \
  /home/ubuntu/work/rpi2/poky/meta-yocto \
  /home/ubuntu/work/rpi2/poky/meta-yocto-bsp \
  /home/ubuntu/work/rpi2/poky/meta-raspberrypi \
  /home/ubuntu/work/rpi2/poky/meta-ssm2603-rpi \
  "
BBLAYERS_NON_REMOVABLE ?= " \
  /home/ubuntu/work/rpi2/poky/meta \
  /home/ubuntu/work/rpi2/poky/meta-yocto \
  "

次いでlocal.confも編集します。local.confは容量の大きいファイルですが、 冒頭部分を下記のように変更するだけで十分です。

#
# This file is your local configuration file and is where all local user settings
# are placed. The comments in this file give some guide to the options a new user
# to the system might want to change but pretty much any configuration option can
# be set in this file. More adventurous users can look at local.conf.extended 
# which contains other examples of configuration which can be placed in this file
# but new users likely won't need any of them initially.
#
# Lines starting with the '#' character are commented out and in some cases the 
# default values are provided as comments to show people example syntax. Enabling
# the option is a question of removing the # character and making any change to the
# variable as required.

MACHINE ?= "raspberrypi2"
BB_NUMBER_THREADS = "6"
PARALLEL_MAKE = "-j 6"
VIDEO_CAMERA = "1"
GPU_MEM = "128"
DL_DIR ?= "${TOPDIR}/../downloads"

IMAGE_INSTALL_append = " alsa-utils \
  "

RPI_SSM260x = "1"

KERNEL_DEVICETREE_append = " rpi-ssm260x-overlay.dtb"

ここではmeta-cqrecorder-rpiを使用しないことにしたため、 その分の記述は消去しています。

以上でbitbakeの準備が完了しました。

Interface誌ではもうこれで9割がた終了している感じですが…… 果たしてそう上手くいくことやら。

bitbakeの実行

bitbakeの実行手順はほんの2ステップです。 ディレクトリを移動して、bitbakeコマンドを叩くだけ。

$ cd ~/work/rpi2/build_ssm
$ bitbake rpi-basic-image

これでイメージが作成できるはずですが、 そう簡単に事が運ぶのでしょうか。

結論から先に言いますと、問題だらけでした。

以下に私がエラーに遭遇したケースついてまとめます。

(Interface誌に書いてないエラーばかりでした。 手探りでやったものですから、所々誤りがあるかもしれません。)

  1. Lubuntu14.04で実行するとエラー

    Lubuntu14.04で実行すると以下の様な文言が表示されます。

    "Unable to determine endianness for architecture 'armv7l'

    環境はLubuntu14.04・Raspberry Pi2です。 armのアーキテクチャがどうのと怒られているので、 もしかしたらラズパイが悪かったのかもしれません。

    WindowsからVirtualBox経由でUbuntu14.04を使えば解決します。

  2. VirtualBoxでストレージを仮想にしているエラー

    bitbakeとは新たにイメージファイルを作成するわけでして、 当然容量がない場合は問題が起きて然るべきです。

    しかし仮想にしていると、最大容量があるのに容量不足だと怒られます。

    "No new tasks can be executed since the disk space monitor action is "STOPTASKS"!"

    Ubuntuのディスク作成時に固定にすれば解決します。

    もしかしたらこの辺りはVirtualBox側の仕様なのかもしれませんね。

  3. ストレージの容量が足りないとエラー

    当たり前といえば当たり前ですが、bitbake時には想像以上の容量を消費します。

    当初16GBぐらいあれば足りるだろうと思ったのですが、 あっという間に食いつぶしてしまいました。

その他様々なエラーが想定されますが、矢鱈と大量のエラーがある場合は、

基本的にエラーの原因はbranchのバージョンが適切で無い場合が多いので、

その周りの環境について調べてみてください。

動作確認

完成したディスクイメージをmicroSD焼いて、Raspberry Piで動作確認してみましょう。

雑誌ではddコマンドを使うことが推奨されていますが、

仮想環境で認識させるのは面倒なので、共有フォルダ経由でホストに移してWindowsのソフトを使用しましょう。

私はDD for Windowsを用いました。

 

多機能オリジナルオーディオ

イントロダクション

さて、第一部ではInterface誌の記事を追ってRaspberry Piを使ったオーディオを作成してみたわけですが、

ただOSを作成するだけでは、あまり面白みがありません。

通常のRaspbianとてヘッドホンからの出力と、 ALSAデバイスドライバ及びAPIを所持しておりますので、

「$ aplay .wavファイル」とするだけでオーディオとして機能します。

どうせオーディオを自作するというなら、何か独自の機能を持たせたいものです。

そこで私が着目したのは音声認識です。

音声認識によって命令を受け取るオーディオを作る。 謂わばSiriによって動くiPhoneのようなものです。

音声認識にあたってはADCが重要となり、Raspberry Piの音声入力はデフォルトだとUSBを経由するしかないのですが、

デバイスドライバを自作するとなるとシリアルから高性能なADCを載せることができるようになります。

またDACもbcm2835に頼らないで済むため、pyaudioによる音楽再生が可能です。

音楽再生をaplayでなくpyaudioによって行うことで、音ファイルの解析を プログラムの起動と平行して行えるようになります。

以上のことからデバイスドライバ開発と音声認識には親和性が高いため、

追加機能として白羽の矢を立てることにしました。

製作レポート

USBカメラによる録音

arecordとは

Linuxカーネルは、オーディオ操作用のデバイスドライバを持っています。

そのドライバがあるからこそ、再生や録音ができる訳です。

標準のドライバはALSAと呼ばれるものですが、ALSAは再生・録音用の関数を持っています。

録音に際してはarecordというコマンドを使用します。

デバイスの確認

arecordは複数のパラメータを指定する必要があります。一例を示すと、

  • フォーマット : -S16_LE
  • デバイス名  : hw:1,0
  • 出力ファイル名: record.wav

デバイス名は

$ arecord -l

で取得することができます。

これでUSBカメラから音声認識を行う準備が完了しました。

音声認識

音声認識エンジンの制定

音声認識エンジンにはjuliusを採用します。

juliusは京都大学等で開発された「汎用大語彙連続音声認識エンジン」であり、

  • 日本語に対応している
  • 動作が軽快
  • ソースがC言語
  • オープンソース

といったRaspberry Piに適した特徴を持っています。

インストール

まずjuliusのソースを取得し、コンパイルします。

$ cd ~/workspace/raspi-audio/download/
$ wget --trust-server-names 'http://osdn.jp/frs/redir.php?m=iij&f=%2Fjulius%2F60273%2Fjulius-4.3.1.tar.gz'
$ tar xvzf julius-4.3.1.tar.gz
$ cd julius-4.3.1/
$ ./configure
$ make

次にディクテーションファイルを取得し、解凍します。

$ cd ..
$ mkdir julius-kits 
$ cd julius-kits
$ wget --trust-server-names 'http://osdn.jp/frs/redir.php?m=iij&f=%2Fjulius%2F60416%2Fdictation-kit-v4.3.1-linux.tgz'
$ tar xvzf dictation-kit-v4.3.1-linux.tgz

Raspberry Piがオーディオを読み込む際の優先順位を変更します。

$ cd /etc/modprobe.d
$ sudo vim alsa-base.conf
options snd slots=snd_usb_audio,snd_bcm2835
options snd_usb_audio index=0
options snd_bcm2835

(参照:Varg

最後に、snd-pcm-ossモジュールの設定をします。

sudo sh -c "echo snd-pcm-oss >> /etc/modules"

以上の手順を踏むことで、juliusを実行できるようになります。

デバッグ用の辞書ファイルが用意されているので、それを用いて実行してみます。

$ cd ~/workspace/raspi-audio/download/julius-4.3.1/julius
$ ALSADEV="plughw:1,0" ~/workspace/raspi-audio/download/julius-4.3.1/julius/julius -C ~/workspace/raspi-audio/download/julius-kits/dictation-kit-v4.3.1-linux/main.jconf -C ~/workspace/raspi-audio/download/julius-kits/dictation-kit-v4.3.1-linux/am-gmm.jconf -nostrip

please speakと表示されたら成功です。カメラに向かって喋ってみましょう。

辞書ファイルの作成

juliusが認識する単語は辞書ファイルによって定義されます。

自分で辞書ファイルを作成してみましょう。

辞書ファイルには特定の記法がありますので、そのうち幾つかを以下に示します。

  • 「認識させたい単語」+Tab+「ローマ字」の組み合わせでリストを構成する。
  • ローマ字は音素毎に半角スペースで区切る。
  • 「ん」の音素は「N」
  • 「し」の音素は「sh i」
  • 「っ」の音素は「q」
  • 最終行を改行コードのみにしない。

作成した辞書ファイルの例を以下に示します。

再生	s a i s e i
停止	t e i sh i

「saisei」「teishi」という声に反応して、juliusが「再生」停止」を認識できるようにしています。

さて、.dic形式の辞書ファイルを作ってみましたが、 辞書ファイルを扱うには設定ファイルも作成する必要があります。

辞書ファイルは以下のようになります。

-w myDic.dic
-v model/lang_m/bccwj.60k.htkdic
-h model/phone_m/jnas-tri-3k16-gid.binhmm
-hlist model/phone_m/logicalTri
-n 5
-output 1
-input mic
-rejecthshort 800
-lv 1500
-demo

.dicファイルを指定している点がポイントです。

.dicファイルと.confファイルが揃ったので、自前の辞書を用意できました。

それでは先程の実行コマンドを、自作辞書を指定して実行してみましょう。

(両ファイルとも場所は任意です。)

$ cd ~/workspace/raspi-audio/download/julius-4.3.1/julius
$ ALSADEV="plughw:1,0" ~/workspace/raspi-audio/download/julius-4.3.1/julius/julius -C ~/workspace/raspi-audio/download/julius-kits/dictation-kit-v4.3.1-linux/myDic.conf -nostrip

「再生」「停止」の呼びかけを認識してくれたでしょうか。

socket通信による外部連携

juliusは認識した単語を外部アプリケーションに渡すことが出来ます。

その際の方式はsocket通信であり、XMLの形式で取得データを送信します。

juliusのサーバを立てるには辞書の.confファイルの末尾に-moduleを加えるだけです。

-w myDic.dic
-v model/lang_m/bccwj.60k.htkdic
-h model/phone_m/jnas-tri-3k16-gid.binhmm
-hlist model/phone_m/logicalTri
-n 5
-output 1
-input mic
-rejecthshort 800
-lv 1500
-demo
-module

クライアントを用意するには幾つかの方法がありますが、pythonが最も簡単に書けます。

クライアント立ち上げファイルとしてjuliusClient.pyを作成してみます。

# -*- coding: utf-8 -*-
import socket

host = 'localhost'
port = 10500

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))

while True:
    res = s.recv(1024)
    print res

通信ができているかどうか、試しに実行してみましょう。

まずはjuliusをバックグラウンドで実行します。別のクライアントPCを用意するならバックグラウンドは不要です。

$ cd ~/workspace/raspi-audio/download/julius-4.3.1/julius
$ ALSADEV="plughw:1,0" ~/workspace/raspi-audio/download/julius-4.3.1/julius/julius -C ~/workspace/raspi-audio/download/julius-kits/dictation-kit-v4.3.1-linux/myDic.conf -nostrip &

読み込み受け付けモードになってからEnterを押すと、無事にバックグラウンドに送られます。

あとは自動でサーバが立ち上がるので、次にクライアントを起こします。

$ python juliusClient.py

さあ、「再生」「停止」と呼びかけましょう。無事に出力されるはずです。

XMLパーサの活用

juliusClient.pyによる出力では、XMLデータそのままが出力されています。

これはXMLパーサを活用することで無駄な情報を排除できます。

ソケットの受け取りからパースまで行うサンプルコードを以下に示します。

if __name__ == "__main__":
	host = 'localhost'
	port = 10500

	from gethtml import news
	news.get()
	from newsparser import parser
	parser.parse("news.txt")
	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.connect((host, port))

	while True:
		res = s.recv(1024)
		if not res.find('WORD') == -1:	
			try:
				ary1 = res.split('WORD')
				ary2 = ary1[1].split('"')
				print ary2[1]
				interpreter(ary2[1])
			except:
				print("error in xml parser.")

パース結果をinterpreter()に送り、あとはそこから分岐することで、任意の命令を下せるようになります。

以上で音声認識についての製作レポートを終わります。

参考文献

  1. Yocto Project, 2016/01/09 9:57取得
  2. 大語彙連続音声認識エンジン Julius, 2016/01/09 9:57取得