ReSpeakerで発話者の音声方向測位

#音源定位とは
音源定位とは、音がどこから聞こえているか、というもので、立体音響やバイノーラルなどを想像して下さい。立体音響は音源定位を行うことで、楽器が後ろから聞こえるように感じたりします。


今回は[ReSpeaker Mic Array v2.0](https://wiki.seeedstudio.com/ReSpeaker_Mic_Array_v2.0/)を使用します。

<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486822/44a922c3-1692-630d-32e5-94d1f305ba51.png">


使い方や、その他オプションも基本全てここに書かれていますが、簡単にまとめておきます。

#音源定位の実装

###環境
ubuntu 18.04
python3

----
###環境構築
```sh

sudo apt-get update
sudo pip install pyusb click
git clone https://github.com/respeaker/usb_4_mic_array.git
cd usb_4_mic_array
sudo python dfu.py --download 6_channels_firmware.bin
```
usb_4_mic_arrayフォルダが作成されたと思います。
この下に以下のコードを作成し実行します。

```python:DOA.py
from tuning import Tuning
import usb.core
import usb.util
import time
 
dev = usb.core.find(idVendor=0x2886, idProduct=0x0018)
 
if dev:
    Mic_tuning = Tuning(dev)
    print(Mic_tuning.direction)
    while True:
        try:
            print(Mic_tuning.direction)
            time.sleep(1)
        except KeyboardInterrupt:
            break
```
出力結果↓

```
~/usb_4_mic_array$ python3 DOA.py 
122
121
120
122
```

####エラーが出る場合
このコードを実行したとき、
ImportError: No module named usb.core 
とでたら

```shell

sudo apt-get install python-usb python3-usb
```
を実行します。

また、permission deniedとでたら、udevでデバイスのアクセス権を与えてあげます。

```shell
sudo touch /etc/udev/rules.d/10-any_name_is_ok.rules
echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="2886", ATTR{idProduct}=="0018", MODE="0666", GROUP="plugdev"' | sudo tee 10-any_name_is_ok.rules
sudo gpasswd -a ユーザー名 plugdev
sudo chmod a+r /etc/udev/rules.d/10-any_name_is_ok.rules
sudo udevadm control --reload-rules
udevadm trigger
```
この後、再起動すると反映されます。

----

##人の声のみに反応させる

ReSpeakerにはたくさんのオプションが存在します。
[オプションのパラメータ一覧(よくある質問のQ1)]
(https://wiki.seeedstudio.com/ReSpeaker_Mic_Array_v2.0/#faq)

例として、SPEECHDETECTEDを使い、人の声のみの音源定位を実装します。
0 = false (no speech detected)
1 = true (speech detected)
なので、以下のようにDOA.pyを改変します。

```python:voise_angle.py
from tuning import Tuning
import usb.core
import usb.util
import time
 
dev = usb.core.find(idVendor=0x2886, idProduct=0x0018)
 
if dev:
    Mic_tuning = Tuning(dev)
    while True:
        try:
            if Mic_tuning.read('SPEECHDETECTED') == 1:
                 print(Mic_tuning.direction)
                 time.sleep(1)
        except KeyboardInterrupt:
            break
```
これで、たとえば手を鳴らしても角度を出力しないようにできます。

---

###注意
これらのコードは、usb_4_mic_array/Tuning.pyを使っているので、このフォルダ以外で実行するにはTuning.pyをフォルダに置くか、コードの中にTuning.pyの関数等を記載する必要があります。

どのフォルダでも使えるように書き換えた例を乗せておきます。

```python:get_angle.py
from time import sleep

import usb
import usb.core
import usb.util
import struct

dev = usb.core.find(idVendor=0x2886,idProduct=0x0018)
TIMEOUT = 100000

# PARAMETERS for sound localization
PARAMETERS = {
    'DOAANGLE': (21, 0, 'int', 359, 0, 'ro', 'DOA angle. Current value. Orientation depends on build configuration.'),
    'SPEECHDETECTED': (19, 22, 'int', 1, 0, 'ro', 'Speech detection status.', '0 = false (no speech detected)',
                       '1 = true (speech detected)'),
    }

def read(param_name):
    try:
        data = PARAMETERS[param_name]
    except KeyError:
        return

    id = data[0]

    cmd = 0x80 | data[1]
    if data[2] == 'int':
        cmd |= 0x40

    length = 8

    response = dev.ctrl_transfer(
        usb.util.CTRL_IN | usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_RECIPIENT_DEVICE,
        0, cmd, id, length, TIMEOUT)

    response = struct.unpack(b'ii', response.tostring())

    if data[2] == 'int':
        result = response[0]
    else:
        result = response[0] * (2. ** response[1])

    return result

# Find angular
if dev:

    while True:
        if read('SPEECHDETECTED') == 1:
            print(read('DOAANGLE'))
            sleep(1)
```

これでどのフォルダ下でも音源定位が出来ると思います。

----

###おまけ：LED制御
好きな場所で以下を実行します(homeでもusb_4_mic_array内でも)

```sh:
git clone https://github.com/respeaker/pixel_ring.git
cd pixel_ring
sudo python setup.py install
sudo python examples/usb_mic_array.py
```
これを実行すると、
wakeupの光り方を３秒
thinkの光り方を３秒
speak光り方を6秒
off光り方を３秒
をします。
その他の光り方は、pixel_ring/pixel_ring/usb_pixel_ring_v2.py を参照して下さい。