RaspberryPiでマイク録音してみる
前回のエントリでヒミツのクマちゃんが占いをしてくれるようになったので、今度は会話したくなってきました。そこでその第一歩としてRaspberryPiにマイクを接続しようと思います。
こちらの記事を参考にしています。
以下のマイクが実績があるとのことだったので購入することにしました。
値段的にはピン形状のマイク(アナログ)でもいいかなと思うのですが、ノイズが結構入るかなと思ったのでUSB接続にしました。
マイクを接続してみる。
マイクをUSBに接続してみてlsusb
で認識状況を確認します。
【接続前】
$ lsusb Bus 001 Device 006: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode) Bus 001 Device 005: ID 2019:1201 PLANEX Bus 001 Device 004: ID 05e3:0608 Genesys Logic, Inc. USB-2.0 4-Port HUB Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp. LAN9500 Ethernet 10/100 Adapter / SMSC9512/9514 Hub Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
【接続後】
$ lsusb Bus 001 Device 007: ID 0d8c:0134 C-Media Electronics, Inc. Bus 001 Device 006: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode) Bus 001 Device 005: ID 2019:1201 PLANEX Bus 001 Device 004: ID 05e3:0608 Genesys Logic, Inc. USB-2.0 4-Port HUB Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp. LAN9500 Ethernet 10/100 Adapter / SMSC9512/9514 Hub Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
比較すると一番上の行に以下のエントリが追加されていることがわかります。
Bus 001 Device 007: ID 0d8c:0134 C-Media Electronics, Inc.
以前使用していたUSBスピーカーもC-Media製だったような気がするのでC-Media製の音声デバイスって多いみたい。
何もしなくてもデバイスの認識はされたような感じですね。
通常であれば
/etc/modprobe.d/alsa-base.conf
を編集して、使用するオーディオモジュールの優先順位の変更して、RaspberryPiのリブート
の処理を行うことになるのですが、実は行わなくても録音は可能です。
alsaを使用して録音してみる。
基本的なテストはCLIでalsaのコマンド群を使用することで確認ができます。
録音に使用できるデバイス一覧の確認
$ arecord -l **** ハードウェアデバイス CAPTURE のリスト **** カード 1: Device [USB PnP Audio Device], デバイス 0: USB Audio [USB Audio] サブデバイス: 1/1 サブデバイス #0: subdevice #0
複数ある場合にはいくつかのデバイスが列挙できますが、今回は1つしか存在していないのでこのような表示になります。
ここで覚えて置かなければならなのは カード番号が1、デバイス番号が0であることです。
先程オーディオモジュールの優先順位を変更を飛ばしたといいましたが、デフォルト値ではなく、デバイス指定を行うので不要でしたということになります。
録音を行う
$ arecord -D plughw:1,0 test.wav 録音中 WAVE 'test.wav' : Unsigned 8 bit, レート 8000 Hz, モノラル ^Cシグナル 割り込み で中断...
wavファイルはデフォルトの設定で録音します(データは8bit、サンプリングレートは8kHzのモノラル)。
コマンドスイッチで-D plughw:1,0
としている部分がデバイスを明示的にしている部分となります。明示的に指定しないとデフォルト値(デバイスの優先順位が高いものを)探しに行くので注意が必要です。
先程のarecord -l
コマンドの結果で、「カード番号が1、デバイス番号が0」と検出されていたので、この値を-D
で渡しています。
再生する
再生に関しては以下となります。
$ aplay test.wav 再生中 WAVE 'test.wav' : Unsigned 8 bit, レート 8000 Hz, モノラル
録音のパラメータも正しく認識されているので問題ありません。
録音ボリューム調整
録音すると音量が小さいことがあります。その場合には以下で調整することになります。
$ amixer sset Mic 46 -c 1 Simple mixer control 'Mic',0 Capabilities: cvolume cvolume-joined cswitch cswitch-joined Capture channels: Mono Limits: Capture 0 - 62 Mono: Capture 46 [74%] [14.62dB] [on]
ここで-cで渡している番号カード番号となるので、arecord -l
コマンドの結果で、「カード番号が1、デバイス番号が0」と検出されていたので、カード番号の1を渡し、-c 1
となります。
最小は0で最大は62まで指定できるようです。
録音ボリュームの値を確認するだけであれば以下で確認できます。
$ amixer sget Mic -c 1 Simple mixer control 'Mic',0 Capabilities: cvolume cvolume-joined cswitch cswitch-joined Capture channels: Mono Limits: Capture 0 - 62 Mono: Capture 46 [74%] [14.62dB] [on]
これでデバイスの基本的な機能の確認ができました。
pythonから録音してみる
これからは毎度のことですが、いつも通りpythonから使用できるかを確認することになります。ネットを調べるとpyaudioというモジュールを使うと良さそうです。
pipでインストールしてみる
ではpip
コマンドを使用してインストールしてみます。
$ pip install pyaudio Downloading/unpacking pyaudio Downloading PyAudio-0.2.9.tar.gz (289kB): 289kB downloaded Running setup.py (path:/tmp/pip-build-uNj4Vs/pyaudio/setup.py) egg_info for package pyaudio warning: no files found matching '*.c' under directory 'test' Installing collected packages: pyaudio Running setup.py install for pyaudio building '_portaudio' extension arm-linux-gnueabihf-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-security -fPIC -I/usr/include/python2.7 -c src/_portaudiomodule.c -o build/temp.linux-armv6l-2.7/src/_portaudiomodule.o src/_portaudiomodule.c:28:20: fatal error: Python.h: そのようなファイルやデ ィレクトリはありません #include "Python.h" ^ compilation terminated. error: command 'arm-linux-gnueabihf-gcc' failed with exit status 1 Complete output from command /usr/bin/python -c "import setuptools, tokenize;__file__='/tmp/pip-build-uNj4Vs/pyaudio/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /tmp/pip-RuGTpw-record/install-record.txt --single-version-externally-managed --compile: running install running build running build_py creating build creating build/lib.linux-armv6l-2.7 copying src/pyaudio.py -> build/lib.linux-armv6l-2.7 running build_ext building '_portaudio' extension creating build/temp.linux-armv6l-2.7 creating build/temp.linux-armv6l-2.7/src arm-linux-gnueabihf-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-security -fPIC -I/usr/include/python2.7 -c src/_portaudiomodule.c -o build/temp.linux-armv6l-2.7/src/_portaudiomodule.o src/_portaudiomodule.c:28:20: fatal error: Python.h: そのようなファイルやディレ クトリはありません #include "Python.h" ^ compilation terminated. error: command 'arm-linux-gnueabihf-gcc' failed with exit status 1 ---------------------------------------- Cleaning up... Command /usr/bin/python -c "import setuptools, tokenize;__file__='/tmp/pip-build-uNj4Vs/pyaudio/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" install --record /tmp/pip-RuGTpw-record/install-record.txt --single-version-externally-managed --compile failed with error code 1 in /tmp/pip-build-uNj4Vs/pyaudio Traceback (most recent call last): File "/usr/bin/pip", line 9, in <module> load_entry_point('pip==1.5.6', 'console_scripts', 'pip')() File "/usr/lib/python2.7/dist-packages/pip/__init__.py", line 248, in main return command.main(cmd_args) File "/usr/lib/python2.7/dist-packages/pip/basecommand.py", line 161, in main text = '\n'.join(complete_log) UnicodeDecodeError: 'ascii' codec can't decode byte 0xe3 in position 57: ordinal not in range(128)
エラー?
先程のサイトをみると
pip will download the PyAudio source and build it for your system. Be sure to install the portaudio library development package (portaudio19-dev) and the python development package (python-all-dev) beforehand. For better isolation from system packages, consider installing PyAudio in a virtualenv.
という記述が。インストール時にビルドするので事前にportaudio19-dev
とpython-all-dev
のパッケージは事前に入れてねということみたいです。PyAudioはportaudioの機能を使用するモジュールなので確かに必要ですね。
今回はお手軽さを目指したいのでapt-getでインストールします。
apt-get
でインストールしてみる
$ sudo apt-get install python-pyaudio python3-pyaudio パッケージリストを読み込んでいます... 完了 依存関係ツリーを作成しています 状態情報を読み取っています... 完了 以下のパッケージが自動でインストールされましたが、もう必要とされていません: gyp libasn1-8-heimdal libc-ares-dev libc-ares2 libgssapi3-heimdal libhcrypto4-heimdal libheimbase1-heimdal libheimntlm0-heimdal libhx509-5-heimdal libjs-node-uuid libjs-underscore libkrb5-26-heimdal libroken18-heimdal libssl-dev libssl-doc libv8-3.14-dev libv8-3.14.5 libwind0-heimdal rlwrap これを削除するには 'apt-get autoremove' を利用してください。 提案パッケージ: python-pyaudio-doc 以下のパッケージが新たにインストールされます: python-pyaudio python3-pyaudio アップグレード: 0 個、新規インストール: 2 個、削除: 0 個、保留: 181 個。 46.8 kB のアーカイブを取得する必要があります。 この操作後に追加で 194 kB のディスク容量が消費されます。 取得:1 http://mirrordirector.raspbian.org/raspbian/ jessie/main python-pyaudio armhf 0.2.8-1 [23.2 kB] 取得:2 http://mirrordirector.raspbian.org/raspbian/ jessie/main python3-pyaudio armhf 0.2.8-1 [23.6 kB] 46.8 kB を 1秒 で取得しました (30.8 kB/s) 以前に未選択のパッケージ python-pyaudio を選択しています。 (データベースを読み込んでいます ... 現在 129542 個のファイルとディレクトリがインストールされています。) .../python-pyaudio_0.2.8-1_armhf.deb を展開する準備をしています ... python-pyaudio (0.2.8-1) を展開しています... 以前に未選択のパッケージ python3-pyaudio を選択しています。 .../python3-pyaudio_0.2.8-1_armhf.deb を展開する準備をしています ... python3-pyaudio (0.2.8-1) を展開しています... python-pyaudio (0.2.8-1) を設定しています ... python3-pyaudio (0.2.8-1) を設定しています ...
python3はまだ使用する気はないですが、一応入れます。
PyAudioで録音を行う
公式サイトのサンプルを実験してみます。
#!/usr/bin/env python # -*- coding: utf-8 -*- """PyAudio example: Record a few seconds of audio and save to a WAVE file.""" import pyaudio import wave CHUNK = 1024 FORMAT = pyaudio.paInt16 CHANNELS = 2 RATE = 44100 RECORD_SECONDS = 5 WAVE_OUTPUT_FILENAME = "output.wav" p = pyaudio.PyAudio() stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK) print("* recording") frames = [] for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)): data = stream.read(CHUNK) frames.append(data) print("* done recording") stream.stop_stream() stream.close() p.terminate() wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb') wf.setnchannels(CHANNELS) wf.setsampwidth(p.get_sample_size(FORMAT)) wf.setframerate(RATE) wf.writeframes(b''.join(frames)) wf.close()
これを実行させると…
$ python pyaudio_sample.py ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave Cannot connect to server socket err = No such file or directory Cannot connect to server request channel jack server is not running or cannot be started Expression 'parameters->channelCount <= maxChans' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 1514 Expression 'ValidateParameters( inputParameters, hostApi, StreamDirection_In )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 2818 Traceback (most recent call last): File "pyaudio_sample.py", line 19, in <module> frames_per_buffer=CHUNK) File "/usr/lib/python2.7/dist-packages/pyaudio.py", line 747, in open stream = Stream(self, *args, **kwargs) File "/usr/lib/python2.7/dist-packages/pyaudio.py", line 442, in __init__ self._stream = pa.open(**arguments) IOError: [Errno Invalid number of channels] -9998
おや?エラーになってしまいました。エラーメッセージをみるとbuffer関連でエラーが出ています。
File "pyaudio_sample.py", line 19, in <module> frames_per_buffer=CHUNK)
いろいろ実験を行った結果録音のパラメータがハードウエアに対応していなかったりするとこのようなエラーがでるようです。そこでパラメータを以下のように変更してみます。
【冒頭の設定部分】
CHUNK = 1024 * 2 FORMAT = pyaudio.paInt16 CHANNELS = 1 RATE = 16000 RECORD_SECONDS = 5 WAVE_OUTPUT_FILENAME = "output.wav"
バッファのサイズを倍、チャンネル数をモノラル、レートを16kHzへ変更してみました。
再度実行を行うと
$ python pyaudio_sample.py ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave Cannot connect to server socket err = No such file or directory Cannot connect to server request channel jack server is not running or cannot be started * recording * done recording
いろいろalsaやportaudioからアラートは出ていますが、無事に録音できました。
PyAudioで再生を行う
PyAudioは再生ももちろんできるのですが… 公式サイトの再生のサンプルを使ってみます。
#!/usr/bin/env python # -*- coding: utf-8 -*- """PyAudio Example: Play a WAVE file.""" import pyaudio import wave import sys CHUNK = 1024 if len(sys.argv) < 2: print("Plays a wave file.\n\nUsage: %s filename.wav" % sys.argv[0]) sys.exit(-1) wf = wave.open(sys.argv[1], 'rb') p = pyaudio.PyAudio() stream = p.open(format=p.get_format_from_width(wf.getsampwidth()), channels=wf.getnchannels(), rate=wf.getframerate(), output=True) data = wf.readframes(CHUNK) while data != '': stream.write(data) data = wf.readframes(CHUNK) stream.stop_stream() stream.close() p.terminate()
これを実行すると…
$ python pyaudio_sample_play.py output.wav ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline ALSA lib pcm_dmix.c:1022:(snd_pcm_dmix_open) unable to open slave Cannot connect to server socket err = No such file or directory Cannot connect to server request channel jack server is not running or cannot be started
こちらもいろいろアラートが表示されますが、再生はできます…しかし、なんかノイズのっています。aplay
で再生するとノイズが乗っていないので再生時にノイズがのっているようですが、原因はわかりませんでした。(処理能力などの問題でしょうか。)
単に再生するだけならpygameでも再生はできるのでそっちを使ってもいいかもしれません。(ループなど再生機能が多機能なので)
今回はpythonから録音ができたので、次は会話ができるように拡張をしていきたいと思います。