Pythonでサウンドを扱う

# Pythonでサウンドを扱う

Pythonでサウンドを扱う方法がいろいろあってよくわからなかったので、ざっくりまとめ

基本的にはこちらのサイト [Playing and Recording Sound in Python](https://realpython.com/playing-and-recording-sound-python/) を参考にした。

## 再生

オーディオ再生するライブラリの一例

* [playsound](#playsound)　WAVとMP3ファイルを再生するためだけの簡単なパッケージ
* [simpleaudio](#simpleaudio)　WAVファイルとNumPyアレイを再生できるライブラリ
* [winsound](#winsound)　WAVファイルとビープ音を再生できる、Windows用ライブラリ
* [python-sounddevice](#python-sounddevice)、[pyaudio](#pyaudio)　WAVファイルを再生するPortAudioライブラリの提供
* [pydub](#pydub)　pyaudioも必要になるが、ffmpegをインストールすることで数行のコードで幅広いオーディオ形式を再生できる

### [playsound](https://pypi.org/project/playsound/)

playsound関数のみが実装されており、WAVファイルもしくはMP3ファイルを指定して再生する。

#### install

```bash
pip install playsound
```

#### usage

```python
from playsound import playsound

playsound("sample.wav")
```

### [simpleaudio](https://simpleaudio.readthedocs.io/en/latest/)

WAVファイルもしくはNumPyアレイを再生する。再生中かどうかの判別も可能。

#### install

```bash
pip install simpleaudio
```

#### usage

```python
import simpleaudio

wav_obj = simpleaudio.WaveObject.from_wave_file("sample.wav")
play_obj = wav_obj.play()
play_obj.wait_done()
```

```python
# 再生中か確認する
if play_obj.is_playing():
	print("still playing")
```

### [winsound](https://docs.python.org/3.6/library/winsound.html)

Windowsの場合、winsoundがデフォルトで使用可能？ WAVファイルの再生およびビープ音の再生が可能。

#### usage

```python
import winsound

winsound.PlaySound("sample.wav", winsound.SND_FILENAME)
```

```python
# ビープ音の再生
import winsound

winsound.Beep(1000, 100) # 1000Hzのビープを100ms再生
```

### [python-sounddevice](https://python-sounddevice.readthedocs.io/en/latest/)

PortAudioライブラリのバインドを提供。オーディオ信号、NumPyアレイを再生/録音するための機能を提供。

#### install

Anacondaを使用している場合、condaパッケージでインストールできる

```bash
conda install -c conda-forge python-sounddevice
```

#### usage

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

wf = wave.open("sample.wav")
fs = wf.getframerate()
data = wf.readframes(wf.getnframes())
data = np.frombuffer(data, dtype='int16')

sd.play(data, fs)
status = sd.wait()
```

上記は一曲全体を一括で扱うが、リアルタイム処理などを行う場合はコールバック処理で短い単位ごとにオーディオ信号を扱う必要がある。

```python
import sounddevice as sd

duration = 5.5

def callback(indata, outdata, frames, time, status):
    if status:
        print(status)
    outdata[:] = indata

with sd.Stream(channels=2, callback=callback):
    sd.sleep(int(duration * 1000))
```

### [pydub](https://github.com/jiaaro/pydub)

#### install

```bash
pip install pydub
```

MP3等のWAV以外のファイルを使用する際には、ffmpegかlibavのインストールも必要。

さらに、オーディオの再生には以下のいずかれをインストールする必要あり（simpleaudioが推奨）

* simpleaudio
* pyaudio
* ffplay (ffmpegに同梱)
* avplay (libavに同梱)

#### usage

```python
from pydub import AudioSegment
from pydub.playback import play

sound = AudioSegment.from_file("sample.wav", format="wav")
play(sound)
```

### [pyaudio](https://people.csail.mit.edu/hubert/pyaudio/)

PortAudioライブラリのバインドを提供。クロスプラットフォーム上で簡単にオーディオ再生、録音が可能。

単純なオーディオの再生は他の方法に比べて複雑なため、単純にファイルを再生するだけであれば他の方法を選択した方がよい。

しかし、より低次元での制御を行うため入出力デバイスの設定やレイテンシの確認などが可能。また、callbackでも記述できるため、比較的複雑な処理を行う際に適している。

#### install

```bash
pip install pyaudio

# condaでも可能
conda install pyaudio
```

#### usage

```python
import pyaudio
import wave
import sys

chunk = 1024

wf = wave.open("sample.wav", "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-sounddevice](#python-sounddevice)　マイクから録音し、NumPyアレイとして保持する
* [pyaudio](#pyaudio)　マイクから録音し、バイナリとして保持する

### [python-sounddevice](https://python-sounddevice.readthedocs.io/en/latest/)

#### usage

```python
import sounddevice as sd
from scipy.io.wavfile import write

record_second = 3
fs = 44100

myrecording = sd.rec(int(record_second * fs), samplerate=fs, channels=2)

write('output.wav', fs, myrecording)
```

### [pyaudio](https://people.csail.mit.edu/hubert/pyaudio/)

#### usage

```python
import pyaudio
import wave

chunk = 1024
format = pyaudio.paInt16
channels = 2
fs = 44100
record_second = 3

p = pyaudio.PyAudio()
stream = p.open(format=format, channels=channels, rate=fs, input=True, frames_per_buffer=chunk)

print("* recording")

frames = []

for i in range(int(fs / chunk * record_second)):
    data = stream.read(chunk)
    frames.append(data)

print("* done recording")

stream.stop_stream()
stream.close()
p.terminate()

wf = wave.open("output.wav", "wb")
wf.setnchannels(channels)
wf.setsampwidth(p.get_sample_size(format))
wf.setframerate(fs)
wf.writeframes(b''.join(frames))
wf.close()
```

## デバイス

再生/録音デバイスを指定したいことがあるので、各ライブラリでの変更方法を確認。

### [python-sounddevice](https://python-sounddevice.readthedocs.io/en/latest/)

使用可能なデバイス、選択されているデバイスを表示

```python
import sounddevice as sd
sd.query_devices()
```

実行例
![b985e173.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320660/1a98fd7f-fe85-d19b-716c-0b94fc53a8f1.png)


ターミナル上で下記コマンドでも可能。

```bash
python -m sounddevice
```

デバイスIDを、`default.device` に設定するか、`play()` や `Stream()` に device引数 として割り当てることで、デバイスの選択が可能

```python
import sounddevice as sd
sd.default.device = 1, 5
```