【DockerのUbuntuコンテナ + Python】 乃木坂46の曲をsin波で生成して出力

この記事はアドベントカレンダー12日目の記事です。

乃木坂46の曲の中で一番好きな「きっかけ」という曲を、sin波で生成してwav形式で出力します。

# 環境構築
## 開発環境
 - MacBookPro 10.14.5
 - Docker fo Mac 2.1.0.3

## ホストの環境構築
### pulseaudioの準備
```
$ brew install pulseaudio
$ pulseaudio --load=module-native-protocol-tcp --exit-idle-time=-1 --daemon
```
## Dockerの環境構築
Dockerのコンテナで処理した音をホストPCから出力できるように環境を構築します。
### Dockerfile
```dockerfile
FROM ubuntu
LABEL maintainer="nyax"
RUN apt-get update && apt-get install -y \
    wget \
    vim \
    pulseaudio \
    python-pip 
RUN pip install --upgrade \
    numpy \
    matplotlib \
    scipy
```
### イメージのビルド
```
$ docker build -t nogi .
```
### コンテナ起動
```
$ docker run -it -e PULSE_SERVER=docker.for.mac.localhost -v ~/.config/pulse:/root/.config/pulse nogi bash
```

### 準備
```
$ apt-get install python-tk
```
いろいろ出てくる、住んでる場所とか答えます。

##### 日本語文字化け対応
```
$ apt-get install locales locales-all
$ export LANG=en_US.UTF-8
```

### テスト
実際に音声を再生できるかテスト

```
$ wget http://www.name-of-this-site.org/coding/wave/txt2wav/la.wav
$ paplay la.wav
```
「ラ♪」って音が鳴ったら、Docker for mac のコンテナ内の処理で出た音をmacで聞くことに成功。

# 実践
## 音階の周波数
[音階周波数](https://tomari.org/main/java/oto.html)に詳しく書いてあります。

| 音階|| 周波数(Hz) | 今回使用 |  補足事項|
|:-------:|:---------:|:---------:|:-----------:|:-----------:|
|  ド | C5|523.251 | ||
|  レ♭  | C#5| 554.365|      ||
|  レ | D5|587.330 |      ||
|  ミ♭  |D#5 | 622.254|   ◯   |ミとして扱う|
| ミ   | E5|659.255 |      ||
|  ファ  |F5 |698.456 |  ◯    ||
|  ソ♭  |F#5 | 739.989|     ||
|  ソ  | G5|783.991 |   ◯    ||
|  ラ♭  | G#5| 830.609|   ◯   |ラとして扱う|
|   ラ | A5|880.000 |      ||
|  シ♭  | A#5|932.328 |    ◯  |シとして扱う|
|   シ |B5 |987.767 |      ||
|   ド |C6 | 1046.502|  ◯    ||

## 波形の生成
数値計算ライブラリを使って波形を生成しsin波(正弦波)を作ります。
以下の式を用いて生成します。


```math
\begin{equation}
n=t \times f_s (t:再生時間、f_s:サンプリング周波数)
\end{equation}
```

sinの式に書き換えると以下のようになります。

```math
\begin{equation}
sin(n)=A sin( \frac{2 \pi f_0 n}{f_s})
\end{equation}
```

```nogi.py
# -*- coding: utf-8 -*-
import pylab
import scipy.io.wavfile
import functools
import numpy as np


def sin (x, f0, fs):
    return np.sin(2 * np.pi * f0 * x / fs)

def generate (sec, freq, rate):
    f = np.vectorize(lambda x: sin(x, freq, rate))
    return f(np.arange(0, sec * rate))


def main():
    #音階
    scale = {
            'sleep':0.0,
            'do':523.251, 'le':587.330, 'mi':622.254,'fa':698.456,
            'so':783.991, 'la': 830.609, 'ci':932.328,'do_':1046.502
        }
    #メロディー
    score = [
                ('mi', 0.25),('fa', 0.25),('so', 0.25),('la',0.25),('ci',0.5),
                ('sleep', 0.05),
                ('mi', 0.25),('fa', 0.25),('so', 0.25),('la',0.25),('ci',0.5),
                ('ci', 0.5),('mi', 0.4),
                ('le', 0.3),('mi',1.0),
                ('sleep', 0.05),
                ('do_',1.0),
                ('sleep', 0.05),
                ('do_',0.25),('la',0.25),('la',0.25),('la',0.5)
            ]
    rate = 16000
    y = np.empty(0)
    for f, t in score:
        y = np.append(y, generate(t, scale[f], rate))
    scipy.io.wavfile.write('kikkake.wav', rate, y)

if __name__ == '__main__':
    main()
```

実行する

```
$ python nogi.py
```
#### 生成したwavファイルの再生
```
$ paplay kikkake.wav
```
流れたわーい！

## 参考文献
- [Dockerコンテナで文字化けが発生した場合の直し方](https://yatta47.hateblo.jp/entry/2016/10/15/114701)
- [Dockerで音を鳴らす](https://abrakatabura.hatenablog.com/entry/2014/08/21/062305)
- [Docker for mac のコンテナ内の処理で出た音をmacで聞く](https://qiita.com/Mco7777/items/18e29b98ddbc2614169b)
- [Python で音楽を作って楽しもう](https://qiita.com/yura/items/e5c6e9527215e0e0524a)
- [[python]　sin波の音をWAV形式で出力する](https://qiita.com/kinaonao/items/c3f2ef224878fbd232f5)

間違えてたらコメントよろしくお願いします🙇‍♂️