スペクトル、スペクトログラムって何だろう？（音声、音の初心者向け）

# はじめに
教授に「音声波形をスペクトルに変換する理由はなんですか？」と聞かれて答えられなかったため、自分なりにまとめてみた備忘録

# 音声波形をスペクトルに変換する理由はなんですか？
音声波形をスペクトルに変換する理由は
「人間の知覚する音に変換するため」

## 物理現象と知覚現象
音声波形は物理現象です。これは、人間に関わらず万物に同じ影響を与えるもの。
物理現象は人の知覚に影響しない現象もあります。例えば光も紫外線、赤外線を光は人間には知覚できません。しかし、数値的には存在します。
ここで対象を光から音にしてみましょう。音も物理現象で表すと、音の大きさ（デシベル）や音の高さ（ヘルツ）で数値的に表せますが人間には知覚できないレベルの音の大きさや音の高さも数値的に存在します。例えば、超音波は周波数が高すぎて人間には知覚できないものです。
さて、音声や音響解析は対象が音声が人間にどのように聞こえているのか、人間は音声をどのように聞くかが対象になります。そのことから音声波形は人がどのように知覚するかが焦点になります。この「人に聞かせる上で、人がどのように音声を認識しているのか」を表すのがスペクトルとなります。

## 人間の音の聞く仕組み
人の耳に話を移してみます。人の耳は空気の振動で音を知覚する。つまり空気がない真空状態では音が認識できません。空気の振動はまず鼓膜で認識され、耳小骨を経由して蝸牛に伝わり音が電気信号に変わります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2423332/9a11b6d9-96e1-f115-990f-aa43abb306cb.png)
耳の構造（参考：さかした耳鼻科　https://sakashita-jibika.com/ear/ear_construction/）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2423332/76ba611e-3a4d-3579-db2e-711ccb58529c.png)
一連の流れ

一般的に人は音の高さは20ヘルツから20000ヘルツまで聞こえると言われてます。この理由は蝸牛の構造に関わります。
蝸牛の構造はパイプオルガンの仕組みがわかると分かりやすく理解できると思います。パイプオルガンはパイプの長さによって音が変わる楽器で、パイプの長さだけ空気が振動することでその長さに応じた高さの音が出されます。蝸牛はパイプオルガンと似ていて、手前の方が高い音を感じやすく奥のほうが低い音になります。この蝸牛の長さだけ人は音の高さを認識できるのです。この蝸牛の長さが20ヘルツから20000ヘルツまで聞こえる理由となります。
蝸牛についてまとめると、蝸牛は人間は音の高さを知覚し、蝸牛は音を周波数ごとに解析する人間の耳のパーツです。

## スペクトル表示の意味
先程、音声や音響解析は対象が音声が人間どのように聞こえているのか、人間は音声をどのように聞くかが対象になると述べました。これは音声波形を、蝸牛が認識する「音の波形の周波数成分」をみたいということになります。
すなわち、音声波形が人間にとってどのような形で認識しているのかを表すのがスペクトル表示です！

# スペクトル表示とは？？
音声波形は縦軸が振幅で横軸が時間で表されます。これをスペクトルに変換すると縦軸が振幅であることは変わりませんが横軸が周波数に変わります。
あれ？時間はどこに行った？、周波数が出できたが何だ？と思いますが、ここでスペクトル変換に関わるフーリエ変換が登場します。
フーリエ変換は、三角関数の足し算であらゆる波を表現できるという考えの下に音声波形はどのような三角関数（周波数）の足し算で分割されているかを解析する手法です。
音声波形にフーリエ変換を施して得られるものに振幅と位相があります。この振幅を縦軸に、周波数を横軸に並べたものがスペクトル表示となります。
### Pythonでのスペクトルの実装

スペクトルを計算する関数

```py
def Spectrum(data):
 #フーリエ変換を行うと得られる値は複素共役であり、片方のデータがあれば十分なため計算速度が速いnp.fft.rfftを用いる
 dataFFT = np.fft.rfft(data)
 #フーリエ変換で得られる値は複素数であるため、その絶対値をとる
 spec = np.abs(dataFFT)
 return spec
```
スペクトルを表示するプログラム

```py
filepath = 'sample.wav'
data ,sr = torchaudio.load(filepath)

spec = Spectrum(data)

#torchtensorからnumpy配列に変換
spec = spec.t().numpy()

#スペクトルの表示
plt.plot(spec)
#人の可聴域は大体20000hz
plt.xlim(0,20000)
```

音声波形をスペクトル表示にした図
![spec.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2423332/930ba333-3231-1b7d-7b06-a295982a0a7a.png)

フーリエ変換して得られた位相は？と思うかもしれません。位相スペクトルという位相を縦軸、周波数を横軸に並べたものがありますが、人の耳は位相に疎いため、音声屋さんは位相スペクトルを用いることが少ないです。そのため、スペクトルというともっぱら振幅スペクトルをさすことになります。

# スペクトルとスペクトログラム
フーリエ変換を用いて音声波形を周波数成分に分解したものがスペクトル。
このスペクトルが時間方向に並んだものがスペクトログラム。これをソナグラムともよびます。
しかし、上に示したフーリエ変換を施すと時間の情報がなくなってしまいます。そこで短時間フーリエ変換を施して時間の情報を付与します。
短時間フーリエ変換について分からない場合は[前の記事](https://qiita.com/ma-szk/items/fe5c3d010b281dcd3537)で説明しているのでぜひ読んでみてください。

スペクトログラムは時間、周波数、振幅の三要素を持つため二次元のグラフでは色が着いたものが多いです。この表現方法だと縦軸が周波数で横軸が時間、色によって振幅を表します。

### Pythonでのスペクトログラムの実装

実装にあたって以下のリンクを参考にしました。

https://colab.research.google.com/github/YutaroOgawa/pytorch_tutorials_jp/blob/main/notebook/7_Audio/7_1_6_audio_preprocessing_tutorial_jp.ipynb

スペクトログラムを表示する関数

```py
def plot_specgram(data, sr):
  data = data.numpy()
  num_channels, num_frames = data.shape  
  fig, ax = plt.subplots(num_channels, 1)
  if num_channels == 1:
    ax = [ax]
  for c in range(num_channels):
    ax[c].specgram(data[c], Fs=sr)

  plt.show(block=False)
```
スペクトログラムを表示する関数

```py
filepath = 'sample.wav'
data ,sr = torchaudio.load(filepath)

plot_specgram(data, sr)
```

スペクトログラムの図
![specg.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2423332/6d2a0b82-a631-fa22-9fe0-f75b1a8bb21c.png)

# まとめ
音声波形をスペクトルに変換する理由は・・「人間の知覚する音に変換するため」
スペクトルの表示は振幅と周波数で表す。
スペクトルを時間方向に並べたスペクトログラム表示がある。

# 終わりに
「音声波形をスペクトルに変換する理由はなんですか？」と聞かれたら、これできっと答えられる・・のかな？
間違いがあればコメントでこっそりと教えてください。
この記事が誰かのお役に立てれば幸いです。