Snowboy Hotword DetectionをmacOSで動かす

# イントロダクション

[Snowboy Hotword Detection](https://snowboy.kitt.ai/)は[KITT.AI](http://kitt.ai/)が提供する、クラウドベースで学習したホットワードのモデルデータをRaspberry Piなどの環境で利用できるようにするライブラリです。個人的利用であれば無償で利用でき、商用利用の場合には有償でライセンスとテクニカルサポートが提供されます（ウェブサイト上では商用利用の際の料金に関しては記載されていません）。Snowboy Hotword Detectionそのものの紹介やRaspberry Piへのインストール方法に関しては以下の記事を参照してください。

☞[Snowboy Hotword DetectionをRaspberry Piで動かす](http://qiita.com/mayfair/items/d16d092328e60f0cac6b)

なお、この記事は、macOS Sierra 10.12.3（16D32）およびPython 3.5.1で確認したものです。環境が異なる場合には、ここで行ったような変更が不要になる、あるいは別の問題が発生することもあります。

# 問題

macOSへのインストール方法に関しては`README.md`で詳しく説明され、あらかじめコンパイルされたライブラリ（osx-x86_64-1.1.0.tar.bz2）もダウンロードできるようになっています。しかしながら、サンプルを実行しようとするとPythonのバージョンによっては以下のようにエラーが発生してしまいます。

```shell-session
$ python -V
Python 3.5.1
$ python demo.py resources/snowboy.umdl 
Traceback (most recent call last):
  File "demo.py", line 1, in <module>
    import snowboydecoder
  File "/Users/mayfair/Downloads/osx-x86_64-1.1.0/snowboydecoder.py", line 5, in <module>
    import snowboydetect
  File "/Users/mayfair/Downloads/osx-x86_64-1.1.0/snowboydetect.py", line 27, in <module>
    _snowboydetect = swig_import_helper()
  File "/Users/mayfair/Downloads/osx-x86_64-1.1.0/snowboydetect.py", line 23, in swig_import_helper
    '_snowboydetect', fp, pathname, description)
  File "/Users/mayfair/Documents/development/python-docs-samples/speech/grpc/env/lib/python3.5/imp.py", line 242, in load_module
    return load_dynamic(name, filename, file)
  File "/Users/mayfair/Documents/development/python-docs-samples/speech/grpc/env/lib/python3.5/imp.py", line 342, in load_dynamic
    return _load(spec)
ImportError: dynamic module does not define module export function (PyInit__snowboydetect)
```

試しにPython 2.7で同じコードを実行すると問題なく動作することから、ライブラリがPython 3.5に対応していないことが分かります。

```shell-session
$ python2.7 demo.py resources/snowboy.umdl 
Listening... Press Ctrl+C to exit
```

# 解決法

まず、自分の環境に合わせてコンパイルするためにSWIGをインストールします。もし、既にインストールしていて3.0.10よりも古い場合には以下のようにしてアップグレードします。

```shell-session
$ swig -version

SWIG Version 3.0.8

Compiled with clang++ [x86_64-apple-darwin16.3.0]

Configured options: +pcre

Please see http://www.swig.org for reporting bugs and further information
Shigeru-Kobayashis-MacBook-Air:Python mayfair$ brew upgrade swig
Warning: You are using OS X 10.12.
We do not provide support for this pre-release version.
You may encounter build failures or other breakages.
Error: swig 3.0.8 already installed
$ brew upgrade swig
```

次に、Snowboy Hotword DetectionのGitHubからソースコードを取得し、swig/Pythonにある`Makefile`をテキストエディタで開き、以下のように変更します。

```cmake:変更前のMakefile
ifeq ($(shell uname), Darwin)
  CXX := clang++
  PYINC := $(shell python-config --includes)
  PYLIBS := $(shell python-config --ldflags)
```

```cmake:変更後のMakefile
ifeq ($(shell uname), Darwin)
  CXX := clang++
  PYINC := $(shell python3.5-config --includes)
  PYLIBS := $(shell python3.5-config --ldflags)
```

以上で準備が整いましたので、`make`を実行してライブラリをビルドし、`_snowboydetect.so`と`snowboydetect.py`を配布されていたものと置き換えて実行します。

```shell-session
$ make
swig -I../../ -c++ -python -o snowboy-detect-swig.cc snowboy-detect-swig.i
clang++ -I/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/include/python3.5m -I/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/include/python3.5m -I../../ -O3 -fPIC -D_GLIBCXX_USE_CXX11_ABI=0 -c snowboy-detect-swig.cc
clang++ -I../../ -O3 -fPIC -D_GLIBCXX_USE_CXX11_ABI=0  -bundle -flat_namespace -undefined suppress snowboy-detect-swig.o \
	../..//lib/osx/libsnowboy-detect.a -L/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/config-3.5m -lpython3.5m -ldl -framework CoreFoundation -lm -ldl -framework Accelerate -o _snowboydetect.so
```

以上で、Python 3.5で実行しても問題なく動作するようになりました。実行後にキーワードとして指定した「snowboy」をマイクに向かって話し、認識された場合には以下のように`INFO:snowboy:Keyword 1 detected`と表示されます。

```shell-session
$ python demo.py resources/snowboy.umdl 
Listening... Press Ctrl+C to exit
INFO:snowboy:Keyword 1 detected at time: 2017-02-13 08:29:40
```

# リファレンス

- [ImportError: dynamic module does not define init function (PyInit__snowboydetect)](https://github.com/Kitt-AI/snowboy/issues/101)