Raspberry Piでsounddeviceを利用するための準備

## What is sounddevice?
[<font color="Blue">sounddevice</font>](https://python-sounddevice.readthedocs.io/)は、[<font color="Blue">PortAudio</font>](http://www.portaudio.com/)ライブラリのバインディングと、[<font color="Blue">NumPy</font>](https://numpy.org/)配列で生成した信号を再生したり、[<font color="Blue">NumPy</font>](https://numpy.org/)配列として録音するためのライブラリです。

## Setting audio output for Raspberry Pi
1. `sudo raspi-config`を実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2275052/88a83139-9fd9-3dc4-7e69-fd9daa8acdfc.png)
1. `System Options`を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2275052/fb44b384-8763-5938-0c14-0092b8c739e7.png)
1. `Audio`を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2275052/f17ead2a-a40a-5a74-9187-c4a12afc00db.png)
1. 出力先を`HDMI`か`Headphones`かを選べます。
1. `alsamixer`を実行することで、音量やディバイスの情報を確認することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2275052/024a58c4-7969-2a44-79dd-f5e335bca5b1.png)

## Check devices on Raspberry Pi
`python3`を実行し[<font color="Blue">sounddevice</font>](https://python-sounddevice.readthedocs.io/)をインポートします。

```python:
import sounddevice as sd
```
次に、利用可能なデバイスの情報を確認します。

```python:
import sounddevice as sd
sd.query_devices()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2275052/92025ee8-2db2-3dbe-4f7a-498a9210bdbc.png)

|    |  Numeric device ID  |  information about the device  |
| :----: | :----: | :----: |
|    | 0 | bcm2835 Headphones: - (hw:0,0), ALSA (0 in, 8 out) |
|    | 1 | sysdefault, ALSA (0 in, 128 out) |
|    | 2 | lavrate, ALSA (0 in, 128 out) |
|    | 3 | samplerate, ALSA (0 in, 128 out) |
|    | 4 | speexrate, ALSA (0 in, 128 out) |
|    | 5 | pulse, ALSA (32 in, 32 out) |
|    | 6 | upmix, ALSA (0 in, 8 out) |
|    | 7 | vdownmix, ALSA (0 in, 6 out) |
|    | 8 | dmix, ALSA (0 in, 2 out) |
|  *  | 9 | default, ALSA (32 in, 32 out) |
ここで、`Numeric device ID`が`9`であるディバイスがデフォルトであることがわかりました。実際、以下の通りデフォルトのディバイスが確認できます。

```python:
import sounddevice as sd
sd.default.device
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2275052/7ab60fa3-4eb3-0c2e-4a57-2a8662ebfa1f.png)
`Numeric device ID`が`9`であるディバイスの詳細な情報を確認してみましょう。

```python:
import sounddevice as sd
sd.query_devices(device=9, kind='output')
sd.query_hostapis(index=0)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2275052/cecad49e-f18a-7d8e-6678-b2bd468819c3.png)
`Numeric device ID`が`9`であるディバイスのサンプリングレートが`44.1kHz`であることがわかりました。それぞれのオプションの意味については[<font color="Blue">query_devices()</font>](https://python-sounddevice.readthedocs.io/en/0.4.4/api/checking-hardware.html#sounddevice.query_devices)を確認してみてください。

## Python3 on Raspberry Pi
`source ~/python_venv/bin/activate`によって設定した仮想環境に入ってから、python3のソースコードを実行します。

https://qiita.com/uene/items/c6ba3e30ba83efeac470#install-pyaudiosounddevice

## Play sound
音を再生をする場合は大きく分けて二つの方法があります。

1. [<font color="Blue">play()</font>](https://python-sounddevice.readthedocs.io/en/0.4.4/api/convenience-functions.html#sounddevice.play)関数を利用する。
1. [<font color="Blue">OutputStream()</font>](https://python-sounddevice.readthedocs.io/en/0.4.4/api/streams.html#sounddevice.OutputStream)関数を利用する。

 [<font color="Blue">play()</font>](https://python-sounddevice.readthedocs.io/en/0.4.4/api/convenience-functions.html#sounddevice.play)関数は便利ですが、小さなスクリプトや対話型使用 (Jupyter notebook 内など) を想定していて、使用例はかなり限定されています。よって、リアルタイム処理などを行う場合は[<font color="Blue">OutputStream()</font>](https://python-sounddevice.readthedocs.io/en/0.4.4/api/streams.html#sounddevice.OutputStream)関数を利用したほうがよいです。なので、今回は[<font color="Blue">OutputStream()</font>](https://python-sounddevice.readthedocs.io/en/0.4.4/api/streams.html#sounddevice.OutputStream)関数を利用して音の再生を行います。

```python:
import sounddevice as sd
import numpy as np

output_device = sd.default.device[1]
output_device_info = sd.query_devices(device=output_device)

volume = 2
freq = 1e3
offset = 0
fs = int(output_device_info['default_samplerate'])

def out_stream_callback(outdata, frames, time, status):
    global freq, fs, offset

    n_samples, n_channels = outdata.shape

    t = np.arange(offset, offset+n_samples) / fs
    offset += n_samples

    for c in range(n_channels):
        outdata[:, c] = volume*np.sin(2*np.pi*freq*t)

with sd.OutputStream(
    samplerate=fs,
    blocksize=2**10,
    device=output_device,
    channels=2,
    dtype='float32',
    # latency=None,
    # extra_settings=None,
    callback=out_stream_callback,
    # finished_callback=None,
    # clip_off=None,
    # dither_off=None,
    # never_drop_input=None,
    # prime_output_buffers_using_stream_callback=None
    ):
    try:
        while True:
            sd.sleep(10)
    except KeyboardInterrupt:
        print('interrupted!')
        pass
```