【リハビリ】Raspiで音がなると周波数に合わせてカラーLEDを点灯する♪

ほんとに久しぶりにRaspiをいじったので、記載しておく.

### 環境

RaspiでVsCode上のJupyter notebookで開発する。
・pythonのインストール（省略）
・Jupyter notebookのインストール
　ターミナルからJupyter notebookをインストールして動かしておいてから、VsCodeのextensionをインストールする。
・pyaudioのインストール
参考
[【Raspberry Pi】USBマイク音声をPythonで取得/録音する方法](https://hellobreak.net/raspberry-pi-microphone-python/)
Jupyter notebook内に以下でインストール出来る

```
!pip install pyaudio
```
以下は、ターミナルでインストール
※ここは、途中yを入力する必要があるので、ターミナルからインストール

```
sudo apt install libportaudio0 libportaudio2 libportaudiocpp0 portaudio19-dev
```
これで、マイクを使えるようになります.
※確認コード省略
### GPIOで光を制御
※前提としてGPIOはつかえる状態
参考
[【ラズパイ電子工作】LEDを点灯させる方法(GPIO使用)](https://denkisekkeijin.com/raspberrypi/pi-led/)
ボードのpin配置
検索すればでてくるが、Raspi4は2017, 2018などで電源やLan配置が異なるが、pin配置は同一.
参考
[Raspberry Pi 4 pinout Archives | eTechnophiles](https://duckduckgo.com/?q=raspi4+pin&t=raspberrypi&iax=images&ia=images&iai=https%3A%2F%2Fwww.etechnophiles.com%2Fwp-content%2Fuploads%2F2021%2F01%2FR-Pi-4-GPIO-Pinout.jpg)
まず、赤色LEDを使いました。
足の長い方がプラス、短い方がGND。赤色LEDは、とりあえずのテストなら抵抗なしでも光る。

とりあえず、LED点滅は上の参考どおりで、以下のコード
ここでは、LED()関数を呼ぶと点滅する。

```py
#必要なモジュールをインポート
import RPi.GPIO as GPIO             #GPIO用のモジュールをインポート
import time                         #時間制御用のモジュールをインポート

#ポート番号の定義
Led_pin = 26                        #変数"Led_pin"に26
#GPIOの設定
GPIO.setmode(GPIO.BCM)              #GPIOのモードを"GPIO.BCM"に設定
GPIO.setup(Led_pin, GPIO.OUT)       #出力モードに設定

def LED():
    #GPIOの電圧を制御
    GPIO.output(Led_pin, GPIO.HIGH)     #GPIO18の出力をHigh(3.3V)にする
    time.sleep(5)                       #5秒間待つ
    GPIO.output(Led_pin, GPIO.LOW)      #GPIO18の出力をLow(0V)にする
    #GPIOをクリーンアップ
    GPIO.cleanup()
```
### カラーLED
今回はカラーLEDも使いたい。そして、PWMで明るさも制御したい。
ということで、PWMの参考。
[Raspberry Pi Pulse Width Modulation (PWM)](https://www.mbtechworks.com/projects/raspberry-pi-pwm.html)
コード的には、参考のまんまだがBCMでは赤色LEDは光るが、BOARDだと光らない
また、以下ではカラーLEDを使うためにpin12,13,26と6を使っている.
また、カラーLEDは、カソード共通とアノード共通があり、今回は、アノード共通を使った.アノード共通のアノードは通常５vへ接続するらしいが、やってみると何もしなくても赤色に輝いていて、色をコントロールしにくい。
そこで、以下のコードでは、LEDを光らせるときだけpin6で電圧を加えるようにした。
これで、安定に消灯も出来るし、青、赤、緑を点灯出来た。
さらに、全部点灯すれば色々な色を点灯できる。
※このアノード共通カラーLEDの使い方は、オリジナルなので自己責任で利用してください。このアノードに２００Ωの保護抵抗を入れました。

```py
#必要なモジュールをインポート
import RPi.GPIO as GPIO             #GPIO用のモジュールをインポート
import time                         #時間制御用のモジュールをインポート

ポート番号の定義
Led_pin_r = 26
Led_pin_g = 12
Led_pin_b = 13
Led_pin_base = 6
  
GPIO.setup(Led_pin_r, GPIO.OUT)
GPIO.setup(Led_pin_g, GPIO.OUT)
GPIO.setup(Led_pin_b, GPIO.OUT)
GPIO.setup(Led_pin_base, GPIO.OUT)

p_r = GPIO.PWM(Led_pin_r, 100)
p_g = GPIO.PWM(Led_pin_g, 100)
p_b = GPIO.PWM(Led_pin_b, 100)
p_base = GPIO.PWM(Led_pin_base, 100)

def LED():
    p_base.start(100)
    p_r.start(100)
    p_g.start(100)
    p_b.start(100)
    time.sleep(3)    
    p_r.ChangeDutyCycle(100)
    p_g.ChangeDutyCycle(0)
    p_b.ChangeDutyCycle(0)
    time.sleep(3)
    p_r.ChangeDutyCycle(0)
    p_g.ChangeDutyCycle(100)
    p_b.ChangeDutyCycle(0)
    time.sleep(3)
    p_r.ChangeDutyCycle(0)
    p_g.ChangeDutyCycle(0)
    p_b.ChangeDutyCycle(100)
    time.sleep(3)
   
    p_r.stop()
    p_g.stop()
    p_b.stop()
    p_base.stop()
```
### BCMとBOARDとは何か
上で、BCMでは光ったが、BOARDでは光らなかったのは、参考のとおり、それぞれGPIBのpin番号の振り方が異なるためでした.今回のpin番号はBOARDの番号ではなく、外側記載のBCMだということです.
参考から引用
「GPIO.BOARD オプションは、プラグのピン番号、つまり、ボード (例: P1) および下の図の中央に印刷されている番号でピンを参照していることを指定します。

GPIO.BCM オプションは、「Broadcom SOC チャネル」番号でピンを参照していることを意味します。これらは、以下の図の外側にある緑色の四角形の「GPIO」の後の番号です。」
参考
[What is the difference between BOARD and BCM for GPIO pin numbering?](https://raspberrypi.stackexchange.com/questions/12966/what-is-the-difference-between-board-and-bcm-for-gpio-pin-numbering)
### 音声に反応してLED()を呼び出す
この部分は、ほぼ参考のコードを使わせていただいています.
追記したのは、data = stream.read(chunk)でoverflowが出て止まってしまうので、exceptionで拾ってstreamを再定義しました。
また、肝心なことですが、大きな音を検出した時に上のLED()関数を呼び出すようにしました。
※ここでは煩雑なのでやりませんが、特定の周波数（ドレミ）に反応して、LED()関数に引数で渡せば、
**音階に合わせて色を変えるプログラム**が、作成出来ます.

参考
[【Raspberry Pi】マイク音声をリアルタイムFFT解析を行いグラフに表示する【Python](https://hellobreak.net/raspberry-pi-sound-fft-realtime/)

```py
mport pyaudio
import wave
import numpy as np
import matplotlib.pyplot as plt

form_1 = pyaudio.paInt16 # 16-bit resolution
chans = 1 # 1 channel
samp_rate = 44100 # 44.1kHz　サンプリング周波数
N = 50 #50
chunk = 1024*N # 2^12 一度に取得するデータ数
record_secs = 3 # 録音する秒数
dev_index = 2 # デバイス番号
wav_output_filename = 'test.wav' # 出力するファイル
sleepTime = 0.1 #0.0001

audio = pyaudio.PyAudio() # create pyaudio instantiation

# ストリームの作成
stream = audio.open(format = form_1,rate = samp_rate,channels = chans, \
                    input_device_index = dev_index,input = True, \
                    frames_per_buffer=chunk)

# x軸
wave_x = np.linspace(0, samp_rate, chunk)
chunk2 = int(chunk/2)
wave_x2 = wave_x[0:chunk2]

print("recording")

# 音声の取得とFFT処理
while True:
    try:
        # 音声データの取得
        data = stream.read(chunk)
        ndarray = np.frombuffer(data, dtype='int16')
        
        # FFT
        wave_y = np.fft.fft(ndarray)
        wave_y = np.abs(wave_y)
        wave_y2 = wave_y[0:chunk2]
        
        # リアルタイムにグラフ表示
#        plt.plot(wave_x2,wave_y2)
#        plt.xlim(0,5000)
#        plt.draw()
#        plt.pause(sleepTime)
#        plt.cla()

        # 周波数抽出
        feature = np.where((wave_y2 > 1e6)&(wave_x2 >100))
        feature = feature[0]
        if feature != [] :
                LED()
#        else:
#                GPIO.output(Led_pin, GPIO.LOW)      #GPIO18の出力をLow(0V)にする
#                GPIO.cleanup()        
        print(feature*(samp_rate/chunk))    
    except KeyboardInterrupt:
        print("Ctrl+Cで停止しました")
        break
    except BaseException:
        print('exception_on_overflow')
        # ストリームの作成
        stream = audio.open(format = form_1,rate = samp_rate,channels = chans, \
                            input_device_index = dev_index,input = True, \
                            frames_per_buffer=chunk)
        # x軸
        wave_x = np.linspace(0, samp_rate, chunk)
        chunk2 = int(chunk/2)
        wave_x2 = wave_x[0:chunk2]
        print("再recording")    
        continue

print("finished recording")

# ストリームの終了
stream.stop_stream()
stream.close()
audio.terminate()
```

### まとめ

・Raspiで久しぶりに遊んでみた
・音に合わせてカラーLEDを光らせてみた
・FFTとPWMを利用すると **「音階に合わせてカラーLEDを光度と色を変化させて光らせる」** が出来る